Merge "Thermal: change WARNING level to EMERGENCY"
diff --git a/Android.bp b/Android.bp
index 090b3c0..b3a2ef8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -158,9 +158,9 @@
         "core/java/android/hardware/IConsumerIrService.aidl",
         "core/java/android/hardware/ISerialManager.aidl",
         "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
-        "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
         "core/java/android/hardware/biometrics/IBiometricService.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
+        "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
@@ -290,12 +290,12 @@
         "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
         "core/java/android/service/gatekeeper/IGateKeeperService.aidl",
         "core/java/android/service/intelligence/IIntelligenceService.aidl",
-
         "core/java/android/service/notification/INotificationListener.aidl",
         "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
         "core/java/android/service/notification/IConditionListener.aidl",
         "core/java/android/service/notification/IConditionProvider.aidl",
         "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
+        "core/java/android/service/sms/IFinancialSmsService.aidl",
         "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
         "core/java/android/service/vr/IVrListener.aidl",
         "core/java/android/service/vr/IVrManager.aidl",
@@ -572,6 +572,7 @@
         "telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
         "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
         "telephony/java/com/android/internal/telephony/IMms.aidl",
+        "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
         "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
         "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
@@ -652,6 +653,8 @@
         "core/java/com/android/server/DropboxLogTags.logtags",
         "core/java/org/chromium/arc/EventLogTags.logtags",
 
+        ":platform-properties",
+
         ":framework-statslog-gen",
     ],
 
@@ -730,8 +733,10 @@
         "netd_aidl_interface-java",
     ],
 
-    // Loaded with System.loadLibrary by android.view.textclassifier
     required: [
+        // TODO: remove gps_debug when the build system propagates "required" properly.
+        "gps_debug.conf",
+        // Loaded with System.loadLibrary by android.view.textclassifier
         "libmedia2_jni",
     ],
 
@@ -779,9 +784,11 @@
 java_library_host {
     name: "inspector-annotation",
     srcs: [
-        "core/java/android/view/inspector/InspectableChildren.java",
         "core/java/android/view/inspector/InspectableNodeName.java",
         "core/java/android/view/inspector/InspectableProperty.java",
+        // Needed for the ResourceId.ID_NULL constant
+        "core/java/android/content/res/ResourceId.java",
+        "core/java/android/annotation/AnyRes.java",
     ],
 }
 
@@ -1719,7 +1726,9 @@
         "core/java/android/annotation/RequiresPermission.java",
         "core/java/android/annotation/SdkConstant.java",
         "core/java/android/annotation/StringDef.java",
+        "core/java/android/annotation/TestApi.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
+        "core/java/com/android/internal/annotations/GuardedBy.java",
     ],
 }
 
diff --git a/Android.mk b/Android.mk
index 770ec20..92e33e9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,55 +73,35 @@
 	( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
 
 # ==== hiddenapi lists =======================================
-.KATI_RESTAT: \
-	$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
-	$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
-	$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
-	$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
-    .KATI_IMPLICIT_OUTPUTS := \
-        $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
-        $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
-        $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
+.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
+$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
     frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-    frameworks/base/config/hiddenapi-light-greylist.txt \
-    frameworks/base/config/hiddenapi-vendor-list.txt \
-    frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
+    frameworks/base/config/hiddenapi-greylist.txt \
+    frameworks/base/config/hiddenapi-greylist-max-p.txt \
+    frameworks/base/config/hiddenapi-greylist-max-o.txt \
     frameworks/base/config/hiddenapi-force-blacklist.txt \
     $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
     $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
     $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
 	frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-	    --input-public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
-	    --input-private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
-	    --input-whitelists $(PRIVATE_WHITELIST_INPUTS) \
-	    --input-greylists \
-	        frameworks/base/config/hiddenapi-light-greylist.txt \
-	        frameworks/base/config/hiddenapi-vendor-list.txt \
-	        frameworks/base/config/hiddenapi-max-sdk-p-blacklist.txt \
-	        <(comm -12 <(sort $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)) \
-	                   $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
-	        $(PRIVATE_GREYLIST_INPUTS) \
-	    --input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \
-	    --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \
-	    --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \
-	    --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \
-	    --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
-	$(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+	    --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
+	    --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+	    --csv $(PRIVATE_FLAGS_INPUTS) \
+	    --greylist frameworks/base/config/hiddenapi-greylist.txt \
+	    --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
+	    --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
+	    --greylist-max-o-ignore-conflicts \
+	        frameworks/base/config/hiddenapi-greylist-max-o.txt \
+	    --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
+	    --output $@.tmp
+	$(call commit-change-for-toc,$@)
 
 $(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
     frameworks/base/tools/hiddenapi/merge_csv.py \
     $(PRIVATE_METADATA_INPUTS)
 	frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
 
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
 $(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
 
 # Include subdirectory makefiles
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6deda0c..478e4fe 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -248,6 +248,9 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.remotedisplay.jar)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/vendor/overlay/ExperimentNavigationBarSlim)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/overlay/ExperimentNavigationBarSlim)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5936ee4..e731138 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -5,7 +5,9 @@
 
 strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
 
-hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+hidden_api_txt_checksorted_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
+
+hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
 
diff --git a/api/current.txt b/api/current.txt
index a3524da..2eb90d5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -150,6 +150,7 @@
     field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
     field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
     field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+    field public static final java.lang.String USE_FULL_SCREEN_INTENT = "android.permission.USE_FULL_SCREEN_INTENT";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
@@ -480,6 +481,10 @@
     field public static final int dashGap = 16843175; // 0x10101a7
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
+    field public static final int dataRetentionTime = 16844189; // 0x101059d
+    field public static final int dataSentOffDevice = 16844186; // 0x101059a
+    field public static final int dataSharedWithThirdParty = 16844187; // 0x101059b
+    field public static final int dataUsedForMonetization = 16844188; // 0x101059c
     field public static final int datePickerDialogTheme = 16843948; // 0x10104ac
     field public static final int datePickerMode = 16843955; // 0x10104b3
     field public static final int datePickerStyle = 16843612; // 0x101035c
@@ -1197,6 +1202,7 @@
     field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
     field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
     field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
+    field public static final int selectionDividerHeight = 16844190; // 0x101059e
     field public static final int sessionService = 16843837; // 0x101043d
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int settingsSliceUri = 16844179; // 0x1010593
@@ -1312,7 +1318,6 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
-    field public static final int supportsAmbientMode = 16844173; // 0x101058d
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -1496,7 +1501,9 @@
     field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
+    field public static final int usageInfoRequired = 16844185; // 0x1010599
     field public static final int use32bitAbi = 16844053; // 0x1010515
+    field public static final int useAppZygote = 16844184; // 0x1010598
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
@@ -3802,6 +3809,7 @@
     method public void overridePendingTransition(int, int);
     method public void postponeEnterTransition();
     method public void recreate();
+    method public void registerActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
     method public void registerForContextMenu(android.view.View);
     method public boolean releaseInstance();
     method public final deprecated void removeDialog(int);
@@ -3879,6 +3887,7 @@
     method public deprecated void stopManagingCursor(android.database.Cursor);
     method public void takeKeyEvents(boolean);
     method public void triggerSearch(java.lang.String, android.os.Bundle);
+    method public void unregisterActivityLifecycleCallbacks(android.app.Application.ActivityLifecycleCallbacks);
     method public void unregisterForContextMenu(android.view.View);
     field public static final int DEFAULT_KEYS_DIALER = 1; // 0x1
     field public static final int DEFAULT_KEYS_DISABLE = 0; // 0x0
@@ -6351,7 +6360,6 @@
     method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
     method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
     method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
-    method public boolean supportsAmbientMode();
     method public boolean supportsMultipleDisplays();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -6510,6 +6518,7 @@
   }
 
   public class DevicePolicyManager {
+    method public void addCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
     method public void addCrossProfileIntentFilter(android.content.ComponentName, android.content.IntentFilter, int);
     method public boolean addCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     method public int addOverrideApn(android.content.ComponentName, android.telephony.data.ApnSetting);
@@ -6539,6 +6548,7 @@
     method public boolean getBluetoothContactSharingDisabled(android.content.ComponentName);
     method public boolean getCameraDisabled(android.content.ComponentName);
     method public deprecated java.lang.String getCertInstallerPackage(android.content.ComponentName) throws java.lang.SecurityException;
+    method public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(android.content.ComponentName);
     method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
     method public boolean getCrossProfileContactsSearchDisabled(android.content.ComponentName);
     method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
@@ -6627,6 +6637,7 @@
     method public int logoutUser(android.content.ComponentName);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
+    method public boolean removeCrossProfileCalendarPackage(android.content.ComponentName, java.lang.String);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
     method public boolean removeKeyPair(android.content.ComponentName, java.lang.String);
     method public boolean removeOverrideApn(android.content.ComponentName, int);
@@ -7681,6 +7692,7 @@
     method protected void prepareView(android.view.View);
     method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo);
     method public void setExecutor(java.util.concurrent.Executor);
+    method public void setOnLightBackground(boolean);
     method public void updateAppWidget(android.widget.RemoteViews);
     method public void updateAppWidgetOptions(android.os.Bundle);
     method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int);
@@ -9547,6 +9559,7 @@
     method public abstract java.io.File getNoBackupFilesDir();
     method public abstract java.io.File getObbDir();
     method public abstract java.io.File[] getObbDirs();
+    method public abstract java.lang.String getOpPackageName();
     method public abstract java.lang.String getPackageCodePath();
     method public abstract android.content.pm.PackageManager getPackageManager();
     method public abstract java.lang.String getPackageName();
@@ -9757,6 +9770,7 @@
     method public java.io.File getNoBackupFilesDir();
     method public java.io.File getObbDir();
     method public java.io.File[] getObbDirs();
+    method public java.lang.String getOpPackageName();
     method public java.lang.String getPackageCodePath();
     method public android.content.pm.PackageManager getPackageManager();
     method public java.lang.String getPackageName();
@@ -10040,6 +10054,7 @@
     field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
     field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT = "android.intent.action.VIEW";
+    field public static final java.lang.String ACTION_DEFINE = "android.intent.action.DEFINE";
     field public static final java.lang.String ACTION_DELETE = "android.intent.action.DELETE";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
     field public static final deprecated java.lang.String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -11166,8 +11181,8 @@
     field public android.content.pm.ProviderInfo[] providers;
     field public android.content.pm.ActivityInfo[] receivers;
     field public android.content.pm.FeatureInfo[] reqFeatures;
-    field public java.lang.String[] requestedPermissions;
-    field public int[] requestedPermissionsFlags;
+    field public deprecated java.lang.String[] requestedPermissions;
+    field public deprecated int[] requestedPermissionsFlags;
     field public android.content.pm.ServiceInfo[] services;
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
@@ -11175,6 +11190,7 @@
     field public android.content.pm.SigningInfo signingInfo;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
+    field public android.content.pm.UsesPermissionInfo[] usesPermissions;
     field public deprecated int versionCode;
     field public java.lang.String versionName;
   }
@@ -11325,7 +11341,7 @@
     method public abstract int checkSignatures(java.lang.String, java.lang.String);
     method public abstract int checkSignatures(int, int);
     method public abstract void clearInstantAppCookie();
-    method public abstract void clearPackagePreferredActivities(java.lang.String);
+    method public abstract deprecated void clearPackagePreferredActivities(java.lang.String);
     method public abstract java.lang.String[] currentToCanonicalPackageNames(java.lang.String[]);
     method public abstract void extendVerificationTimeout(int, int, long);
     method public abstract android.graphics.drawable.Drawable getActivityBanner(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11369,8 +11385,8 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
     method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
-    method public abstract java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
+    method public abstract deprecated int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
+    method public abstract deprecated java.util.List<android.content.pm.PackageInfo> getPreferredPackages(int);
     method public abstract android.content.pm.ProviderInfo getProviderInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ActivityInfo getReceiverInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForActivity(android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11650,6 +11666,7 @@
     field public java.lang.String group;
     field public java.lang.CharSequence nonLocalizedDescription;
     field public deprecated int protectionLevel;
+    field public boolean usageInfoRequired;
   }
 
   public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -11712,6 +11729,7 @@
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1
+    field public static final int FLAG_USE_APP_ZYGOTE = 8; // 0x8
     field public int flags;
     field public java.lang.String permission;
   }
@@ -11828,6 +11846,28 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
   }
 
+  public final class UsesPermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDataRetention();
+    method public int getDataRetentionWeeks();
+    method public int getDataSentOffDevice();
+    method public int getDataSharedWithThirdParty();
+    method public int getDataUsedForMonetization();
+    method public int getFlags();
+    method public java.lang.String getPermission();
+    field public static final android.os.Parcelable.Creator<android.content.pm.UsesPermissionInfo> CREATOR;
+    field public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 2; // 0x2
+    field public static final int RETENTION_NOT_RETAINED = 1; // 0x1
+    field public static final int RETENTION_SPECIFIED = 4; // 0x4
+    field public static final int RETENTION_UNDEFINED = 0; // 0x0
+    field public static final int RETENTION_UNLIMITED = 3; // 0x3
+    field public static final int RETENTION_USER_SELECTED = 2; // 0x2
+    field public static final int USAGE_NO = 3; // 0x3
+    field public static final int USAGE_UNDEFINED = 0; // 0x0
+    field public static final int USAGE_USER_TRIGGERED = 2; // 0x2
+    field public static final int USAGE_YES = 1; // 0x1
+  }
+
   public final class VersionedPackage implements android.os.Parcelable {
     ctor public VersionedPackage(java.lang.String, int);
     ctor public VersionedPackage(java.lang.String, long);
@@ -13689,6 +13729,8 @@
   public abstract class ColorSpace {
     method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[]);
     method public static android.graphics.ColorSpace adapt(android.graphics.ColorSpace, float[], android.graphics.ColorSpace.Adaptation);
+    method public static float[] cctToIlluminantdXyz(int);
+    method public static float[] chromaticAdaptation(android.graphics.ColorSpace.Adaptation, float[], float[]);
     method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace);
     method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace, android.graphics.ColorSpace, android.graphics.ColorSpace.RenderIntent);
     method public static android.graphics.ColorSpace.Connector connect(android.graphics.ColorSpace);
@@ -13937,6 +13979,7 @@
   }
 
   public final class Insets {
+    method public static android.graphics.Insets add(android.graphics.Insets, android.graphics.Insets);
     method public static android.graphics.Insets of(int, int, int, int);
     method public static android.graphics.Insets of(android.graphics.Rect);
     field public static final android.graphics.Insets NONE;
@@ -14689,6 +14732,7 @@
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
+    method public long getUniqueId();
     method public int getWidth();
     method public boolean hasDisplayList();
     method public boolean hasIdentityMatrix();
@@ -14822,6 +14866,7 @@
     ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface.CustomFallbackBuilder addCustomFallback(android.graphics.fonts.FontFamily);
     method public android.graphics.Typeface build();
+    method public static int getMaxCustomFallbackCount();
     method public android.graphics.Typeface.CustomFallbackBuilder setStyle(android.graphics.fonts.FontStyle);
     method public android.graphics.Typeface.CustomFallbackBuilder setSystemFallback(java.lang.String);
   }
@@ -15041,6 +15086,7 @@
     method public void invalidateSelf();
     method public boolean isAutoMirrored();
     method public boolean isFilterBitmap();
+    method public boolean isProjected();
     method public boolean isStateful();
     method public final boolean isVisible();
     method public void jumpToCurrentState();
@@ -23457,6 +23503,7 @@
   }
 
   public class ExifInterface {
+    ctor public ExifInterface(java.io.File) throws java.io.IOException;
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     ctor public ExifInterface(java.io.FileDescriptor) throws java.io.IOException;
     ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
@@ -23464,11 +23511,13 @@
     method public java.lang.String getAttribute(java.lang.String);
     method public double getAttributeDouble(java.lang.String, double);
     method public int getAttributeInt(java.lang.String, int);
+    method public long[] getAttributeRange(java.lang.String);
     method public boolean getLatLong(float[]);
     method public byte[] getThumbnail();
     method public android.graphics.Bitmap getThumbnailBitmap();
     method public byte[] getThumbnailBytes();
     method public long[] getThumbnailRange();
+    method public boolean hasAttribute(java.lang.String);
     method public boolean hasThumbnail();
     method public boolean isThumbnailCompressed();
     method public void saveAttributes() throws java.io.IOException;
@@ -24503,16 +24552,30 @@
 
   public final class MediaFormat {
     ctor public MediaFormat();
+    ctor public MediaFormat(android.media.MediaFormat);
+    method public boolean containsFeature(java.lang.String);
     method public boolean containsKey(java.lang.String);
     method public static android.media.MediaFormat createAudioFormat(java.lang.String, int, int);
     method public static android.media.MediaFormat createSubtitleFormat(java.lang.String, java.lang.String);
     method public static android.media.MediaFormat createVideoFormat(java.lang.String, int, int);
     method public java.nio.ByteBuffer getByteBuffer(java.lang.String);
+    method public java.nio.ByteBuffer getByteBuffer(java.lang.String, java.nio.ByteBuffer);
     method public boolean getFeatureEnabled(java.lang.String);
+    method public java.util.Set<java.lang.String> getFeatures();
     method public float getFloat(java.lang.String);
+    method public float getFloat(java.lang.String, float);
     method public int getInteger(java.lang.String);
+    method public int getInteger(java.lang.String, int);
+    method public java.util.Set<java.lang.String> getKeys();
     method public long getLong(java.lang.String);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.Number getNumber(java.lang.String);
+    method public java.lang.Number getNumber(java.lang.String, java.lang.Number);
     method public java.lang.String getString(java.lang.String);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public int getValueTypeForKey(java.lang.String);
+    method public void removeFeature(java.lang.String);
+    method public void removeKey(java.lang.String);
     method public void setByteBuffer(java.lang.String, java.nio.ByteBuffer);
     method public void setFeatureEnabled(java.lang.String, boolean);
     method public void setFloat(java.lang.String, float);
@@ -24617,6 +24680,12 @@
     field public static final java.lang.String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
     field public static final java.lang.String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     field public static final java.lang.String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
+    field public static final int TYPE_FLOAT = 3; // 0x3
+    field public static final int TYPE_INTEGER = 1; // 0x1
+    field public static final int TYPE_LONG = 2; // 0x2
+    field public static final int TYPE_NULL = 0; // 0x0
+    field public static final int TYPE_STRING = 4; // 0x4
   }
 
   public final class MediaMetadata implements android.os.Parcelable {
@@ -29008,20 +29077,21 @@
 
   public class WifiManager {
     method public deprecated int addNetwork(android.net.wifi.WifiConfiguration);
-    method public boolean addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+    method public int addNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
     method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
     method public static int calculateSignalLevel(int, int);
     method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
     method public static int compareSignalLevel(int, int);
     method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
-    method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
+    method public deprecated android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
     method public deprecated boolean disableNetwork(int);
     method public deprecated boolean disconnect();
     method public deprecated boolean enableNetwork(int, boolean);
     method public deprecated java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
     method public android.net.wifi.WifiInfo getConnectionInfo();
     method public android.net.DhcpInfo getDhcpInfo();
+    method public int getMaxNumberOfNetworkSuggestionsPerApp();
     method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations();
     method public java.util.List<android.net.wifi.ScanResult> getScanResults();
     method public int getWifiState();
@@ -29040,7 +29110,7 @@
     method public deprecated boolean reassociate();
     method public deprecated boolean reconnect();
     method public deprecated boolean removeNetwork(int);
-    method public boolean removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
+    method public int removeNetworkSuggestions(java.util.List<android.net.wifi.WifiNetworkSuggestion>);
     method public void removePasspointConfiguration(java.lang.String);
     method public deprecated boolean saveConfiguration();
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
@@ -29069,11 +29139,17 @@
     field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
     field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
     field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2; // 0x2
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3; // 0x3
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1; // 0x1
+    field public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4; // 0x4
+    field public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0; // 0x0
     field public static final deprecated java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
     field public static final deprecated java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
-    field public static final int WIFI_MODE_FULL = 1; // 0x1
+    field public static final deprecated int WIFI_MODE_FULL = 1; // 0x1
     field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3
-    field public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2
+    field public static final int WIFI_MODE_FULL_LOW_LATENCY = 4; // 0x4
+    field public static final deprecated int WIFI_MODE_SCAN_ONLY = 2; // 0x2
     field public static final java.lang.String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
     field public static final int WIFI_STATE_DISABLED = 1; // 0x1
     field public static final int WIFI_STATE_DISABLING = 0; // 0x0
@@ -29110,6 +29186,9 @@
     method public void setReferenceCounted(boolean);
   }
 
+  public static abstract class WifiManager.NetworkSuggestionsStatusCode implements java.lang.annotation.Annotation {
+  }
+
   public class WifiManager.WifiLock {
     method public void acquire();
     method public boolean isHeld();
@@ -29401,11 +29480,24 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR;
+    field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1
+    field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2
+    field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0
     field public java.lang.String deviceAddress;
     field public int groupOwnerIntent;
     field public android.net.wifi.WpsInfo wps;
   }
 
+  public static final class WifiP2pConfig.Builder {
+    ctor public WifiP2pConfig.Builder();
+    method public android.net.wifi.p2p.WifiP2pConfig build();
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(android.net.MacAddress);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOwnerBand(int);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(java.lang.String);
+    method public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(java.lang.String);
+  }
+
   public class WifiP2pDevice implements android.os.Parcelable {
     ctor public WifiP2pDevice();
     ctor public WifiP2pDevice(android.net.wifi.p2p.WifiP2pDevice);
@@ -29472,6 +29564,7 @@
     method public void clearServiceRequests(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void connect(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
+    method public void createGroup(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pConfig, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void discoverPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener);
@@ -29479,7 +29572,10 @@
     method public void removeLocalService(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceInfo, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener);
+    method public void requestDiscoveryState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener);
     method public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener);
+    method public void requestNetworkInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener);
+    method public void requestP2pState(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.P2pStateListener);
     method public void requestPeers(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.PeerListListener);
     method public void setDnsSdResponseListeners(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.DnsSdServiceResponseListener, android.net.wifi.p2p.WifiP2pManager.DnsSdTxtRecordListener);
     method public void setServiceResponseListener(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ServiceResponseListener);
@@ -29524,6 +29620,10 @@
     method public abstract void onConnectionInfoAvailable(android.net.wifi.p2p.WifiP2pInfo);
   }
 
+  public static abstract interface WifiP2pManager.DiscoveryStateListener {
+    method public abstract void onDiscoveryStateAvailable(int);
+  }
+
   public static abstract interface WifiP2pManager.DnsSdServiceResponseListener {
     method public abstract void onDnsSdServiceAvailable(java.lang.String, java.lang.String, android.net.wifi.p2p.WifiP2pDevice);
   }
@@ -29536,6 +29636,14 @@
     method public abstract void onGroupInfoAvailable(android.net.wifi.p2p.WifiP2pGroup);
   }
 
+  public static abstract interface WifiP2pManager.NetworkInfoListener {
+    method public abstract void onNetworkInfoAvailable(android.net.NetworkInfo);
+  }
+
+  public static abstract interface WifiP2pManager.P2pStateListener {
+    method public abstract void onP2pStateAvailable(int);
+  }
+
   public static abstract interface WifiP2pManager.PeerListListener {
     method public abstract void onPeersAvailable(android.net.wifi.p2p.WifiP2pDeviceList);
   }
@@ -33452,6 +33560,7 @@
     method public static boolean isExternalStorageRemovable();
     method public static boolean isExternalStorageRemovable(java.io.File);
     field public static java.lang.String DIRECTORY_ALARMS;
+    field public static java.lang.String DIRECTORY_AUDIOBOOKS;
     field public static java.lang.String DIRECTORY_DCIM;
     field public static java.lang.String DIRECTORY_DOCUMENTS;
     field public static java.lang.String DIRECTORY_DOWNLOADS;
@@ -37211,6 +37320,7 @@
     method public static java.lang.String getVolumeName(android.net.Uri);
     method public static android.provider.MediaStore.PendingSession openPending(android.content.Context, android.net.Uri);
     method public static android.net.Uri setIncludePending(android.net.Uri);
+    method public static android.net.Uri setRequireOriginal(android.net.Uri);
     field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
     field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE";
     field public static final java.lang.String ACTION_REVIEW = "android.provider.action.REVIEW";
@@ -37308,6 +37418,7 @@
     field public static final java.lang.String COMPOSER = "composer";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String IS_ALARM = "is_alarm";
+    field public static final java.lang.String IS_AUDIOBOOK = "is_audiobook";
     field public static final java.lang.String IS_MUSIC = "is_music";
     field public static final java.lang.String IS_NOTIFICATION = "is_notification";
     field public static final java.lang.String IS_PODCAST = "is_podcast";
@@ -37387,6 +37498,17 @@
     field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
   }
 
+  public static abstract interface MediaStore.DownloadColumns implements android.provider.MediaStore.MediaColumns {
+    field public static final java.lang.String DOWNLOAD_URI = "download_uri";
+    field public static final java.lang.String REFERER_URI = "referer_uri";
+  }
+
+  public static final class MediaStore.Downloads implements android.provider.MediaStore.DownloadColumns {
+    method public static android.net.Uri getContentUri(java.lang.String);
+    field public static final android.net.Uri EXTERNAL_CONTENT_URI;
+    field public static final android.net.Uri INTERNAL_CONTENT_URI;
+  }
+
   public static final class MediaStore.Files {
     ctor public MediaStore.Files();
     method public static android.net.Uri getContentUri(java.lang.String);
@@ -37415,8 +37537,8 @@
     field public static final java.lang.String DATE_TAKEN = "datetaken";
     field public static final java.lang.String DESCRIPTION = "description";
     field public static final java.lang.String IS_PRIVATE = "isprivate";
-    field public static final java.lang.String LATITUDE = "latitude";
-    field public static final java.lang.String LONGITUDE = "longitude";
+    field public static final deprecated java.lang.String LATITUDE = "latitude";
+    field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String ORIENTATION = "orientation";
     field public static final deprecated java.lang.String PICASA_ID = "picasa_id";
@@ -37478,7 +37600,9 @@
 
   public static class MediaStore.PendingParams {
     ctor public MediaStore.PendingParams(android.net.Uri, java.lang.String, java.lang.String);
+    method public void setDownloadUri(android.net.Uri);
     method public void setPrimaryDirectory(java.lang.String);
+    method public void setRefererUri(android.net.Uri);
     method public void setSecondaryDirectory(java.lang.String);
   }
 
@@ -37538,8 +37662,8 @@
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String IS_PRIVATE = "isprivate";
     field public static final java.lang.String LANGUAGE = "language";
-    field public static final java.lang.String LATITUDE = "latitude";
-    field public static final java.lang.String LONGITUDE = "longitude";
+    field public static final deprecated java.lang.String LATITUDE = "latitude";
+    field public static final deprecated java.lang.String LONGITUDE = "longitude";
     field public static final deprecated java.lang.String MINI_THUMB_MAGIC = "mini_thumb_magic";
     field public static final java.lang.String RESOLUTION = "resolution";
     field public static final java.lang.String TAGS = "tags";
@@ -40858,11 +40982,9 @@
     method public int getDesiredMinimumHeight();
     method public int getDesiredMinimumWidth();
     method public android.view.SurfaceHolder getSurfaceHolder();
-    method public boolean isInAmbientMode();
     method public boolean isPreview();
     method public boolean isVisible();
     method public void notifyColorsChanged();
-    method public void onAmbientModeChanged(boolean, boolean);
     method public void onApplyWindowInsets(android.view.WindowInsets);
     method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
     method public android.app.WallpaperColors onComputeColors();
@@ -42802,6 +42924,19 @@
     field public static final int BAND_9 = 9; // 0x9
   }
 
+  public final class AvailableNetworkInfo implements android.os.Parcelable {
+    ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+    method public int describeContents();
+    method public java.util.List<java.lang.String> getMccMncs();
+    method public int getPriority();
+    method public int getSubId();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.AvailableNetworkInfo> CREATOR;
+    field public static final int PRIORITY_HIGH = 1; // 0x1
+    field public static final int PRIORITY_LOW = 3; // 0x3
+    field public static final int PRIORITY_MED = 2; // 0x2
+  }
+
   public class CarrierConfigManager {
     method public android.os.PersistableBundle getConfig();
     method public android.os.PersistableBundle getConfigForSubId(int);
@@ -43623,7 +43758,7 @@
     method public static int getDefaultVoiceSubscriptionId();
     method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
-    method public static int[] getSubscriptionIds(int);
+    method public int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public boolean isActiveSubscriptionId(int);
     method public boolean isNetworkRoaming(int);
@@ -43780,6 +43915,7 @@
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
     method public deprecated void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
+    method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
     field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
@@ -43842,6 +43978,7 @@
     field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
     field public static final int NETWORK_TYPE_IWLAN = 18; // 0x12
     field public static final int NETWORK_TYPE_LTE = 13; // 0xd
+    field public static final int NETWORK_TYPE_NR = 20; // 0x14
     field public static final int NETWORK_TYPE_TD_SCDMA = 17; // 0x11
     field public static final int NETWORK_TYPE_UMTS = 3; // 0x3
     field public static final int NETWORK_TYPE_UNKNOWN = 0; // 0x0
@@ -45620,9 +45757,14 @@
     method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
   }
 
-  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+  public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan android.text.ParcelableSpan {
     ctor public LineHeightSpan.Standard(int);
+    ctor public LineHeightSpan.Standard(android.os.Parcel);
     method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getSpanTypeId();
+    method public void writeToParcel(android.os.Parcel, int);
   }
 
   public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
@@ -45746,6 +45888,7 @@
     method public java.util.Locale getLocaleObject();
     method public int getSpanTypeId();
     method public java.lang.String[] getSuggestions();
+    method public int getUnderlineColor();
     method public void setFlags(int);
     method public void updateDrawState(android.text.TextPaint);
     method public void writeToParcel(android.os.Parcel, int);
@@ -47746,6 +47889,7 @@
     method public final boolean isFunctionPressed();
     method public static final boolean isGamepadButton(int);
     method public final boolean isLongPress();
+    method public static final boolean isMediaSessionKey(int);
     method public final boolean isMetaPressed();
     method public static boolean isModifierKey(int);
     method public final boolean isNumLockOn();
@@ -48978,10 +49122,12 @@
     method protected int getTopPaddingOffset();
     method public android.view.TouchDelegate getTouchDelegate();
     method public java.util.ArrayList<android.view.View> getTouchables();
+    method public float getTransitionAlpha();
     method public java.lang.String getTransitionName();
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
+    method public long getUniqueDrawingId();
     method public int getVerticalFadingEdgeLength();
     method public int getVerticalScrollbarPosition();
     method public int getVerticalScrollbarWidth();
@@ -49186,6 +49332,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
+    method public void setAnimationMatrix(android.graphics.Matrix);
     method public void setAutofillHints(java.lang.String...);
     method public void setAutofillId(android.view.autofill.AutofillId);
     method public void setBackground(android.graphics.drawable.Drawable);
@@ -49237,6 +49384,7 @@
     method public void setLayoutDirection(int);
     method public void setLayoutParams(android.view.ViewGroup.LayoutParams);
     method public final void setLeft(int);
+    method public final void setLeftTopRightBottom(int, int, int, int);
     method public void setLongClickable(boolean);
     method protected final void setMeasuredDimension(int, int);
     method public void setMinimumHeight(int);
@@ -49303,6 +49451,7 @@
     method public void setTooltipText(java.lang.CharSequence);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
+    method public void setTransitionAlpha(float);
     method public final void setTransitionName(java.lang.String);
     method public void setTranslationX(float);
     method public void setTranslationY(float);
@@ -49776,6 +49925,7 @@
     method public deprecated boolean isAnimationCacheEnabled();
     method protected boolean isChildrenDrawingOrderEnabled();
     method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
+    method public boolean isLayoutSuppressed();
     method public boolean isMotionEventSplittingEnabled();
     method public boolean isTransitionGroup();
     method public final void layout(int, int, int, int);
@@ -49841,6 +49991,7 @@
     method public android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
     method public void startLayoutAnimation();
     method public void startViewTransition(android.view.View);
+    method public void suppressLayout(boolean);
     method public void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams);
     field protected static final int CLIP_TO_PADDING_MASK = 34; // 0x22
     field public static final int FOCUS_AFTER_DESCENDANTS = 262144; // 0x40000
@@ -51854,10 +52005,90 @@
 
 }
 
+package android.view.inspector {
+
+  public abstract interface InspectionCompanion<T> {
+    method public default java.lang.String getNodeName();
+    method public abstract void mapProperties(android.view.inspector.PropertyMapper);
+    method public abstract void readProperties(T, android.view.inspector.PropertyReader);
+  }
+
+  public static class InspectionCompanion.UninitializedPropertyMapException extends java.lang.RuntimeException {
+    ctor public InspectionCompanion.UninitializedPropertyMapException();
+  }
+
+  public final class IntEnumMapping {
+    method public java.lang.String nameOf(int);
+  }
+
+  public static final class IntEnumMapping.Builder {
+    ctor public IntEnumMapping.Builder();
+    method public android.view.inspector.IntEnumMapping.Builder addValue(java.lang.String, int);
+    method public android.view.inspector.IntEnumMapping build();
+    method public void clear();
+  }
+
+  public final class IntFlagMapping {
+    method public java.lang.String[] namesOf(int);
+  }
+
+  public static final class IntFlagMapping.Builder {
+    ctor public IntFlagMapping.Builder();
+    method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int);
+    method public android.view.inspector.IntFlagMapping.Builder addFlag(java.lang.String, int, int);
+    method public android.view.inspector.IntFlagMapping build();
+    method public void clear();
+  }
+
+  public abstract interface PropertyMapper {
+    method public abstract int mapBoolean(java.lang.String, int);
+    method public abstract int mapByte(java.lang.String, int);
+    method public abstract int mapChar(java.lang.String, int);
+    method public abstract int mapColor(java.lang.String, int);
+    method public abstract int mapDouble(java.lang.String, int);
+    method public abstract int mapFloat(java.lang.String, int);
+    method public abstract int mapGravity(java.lang.String, int);
+    method public abstract int mapInt(java.lang.String, int);
+    method public abstract int mapIntEnum(java.lang.String, int, android.view.inspector.IntEnumMapping);
+    method public abstract int mapIntFlag(java.lang.String, int, android.view.inspector.IntFlagMapping);
+    method public abstract int mapLong(java.lang.String, int);
+    method public abstract int mapObject(java.lang.String, int);
+    method public abstract int mapShort(java.lang.String, int);
+  }
+
+  public static class PropertyMapper.PropertyConflictException extends java.lang.RuntimeException {
+    ctor public PropertyMapper.PropertyConflictException(java.lang.String, java.lang.String, java.lang.String);
+  }
+
+  public abstract interface PropertyReader {
+    method public abstract void readBoolean(int, boolean);
+    method public abstract void readByte(int, byte);
+    method public abstract void readChar(int, char);
+    method public abstract void readColor(int, int);
+    method public abstract void readColor(int, long);
+    method public abstract void readColor(int, android.graphics.Color);
+    method public abstract void readDouble(int, double);
+    method public abstract void readFloat(int, float);
+    method public abstract void readGravity(int, int);
+    method public abstract void readInt(int, int);
+    method public abstract void readIntEnum(int, int);
+    method public abstract void readIntFlag(int, int);
+    method public abstract void readLong(int, long);
+    method public abstract void readObject(int, java.lang.Object);
+    method public abstract void readShort(int, short);
+  }
+
+  public static class PropertyReader.PropertyTypeMismatchException extends java.lang.RuntimeException {
+    ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String, java.lang.String);
+    ctor public PropertyReader.PropertyTypeMismatchException(int, java.lang.String, java.lang.String);
+  }
+
+}
+
 package android.view.intelligence {
 
-  public final class IntelligenceManager {
-    method public android.content.ComponentName getIntelligenceServiceComponentName();
+  public final class ContentCaptureManager {
+    method public android.content.ComponentName getServiceComponentName();
     method public boolean isContentCaptureEnabled();
     method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
     method public void notifyViewAppeared(android.view.ViewStructure);
@@ -54488,6 +54719,7 @@
     method public java.lang.String[] getDisplayedValues();
     method public int getMaxValue();
     method public int getMinValue();
+    method public int getSelectionDividerHeight();
     method public int getValue();
     method public boolean getWrapSelectorWheel();
     method public void setDisplayedValues(java.lang.String[]);
@@ -54497,6 +54729,7 @@
     method public void setOnLongPressUpdateInterval(long);
     method public void setOnScrollListener(android.widget.NumberPicker.OnScrollListener);
     method public void setOnValueChangedListener(android.widget.NumberPicker.OnValueChangeListener);
+    method public void setSelectionDividerHeight(int);
     method public void setValue(int);
     method public void setWrapSelectorWheel(boolean);
   }
@@ -54849,6 +55082,7 @@
     method public void setInt(int, java.lang.String, int);
     method public void setIntent(int, java.lang.String, android.content.Intent);
     method public void setLabelFor(int, int);
+    method public void setLightBackgroundLayoutId(int);
     method public void setLong(int, java.lang.String, long);
     method public void setOnClickFillInIntent(int, android.content.Intent);
     method public void setOnClickPendingIntent(int, android.app.PendingIntent);
@@ -55480,6 +55714,7 @@
     method public java.lang.CharSequence getText();
     method public android.view.textclassifier.TextClassifier getTextClassifier();
     method public final android.content.res.ColorStateList getTextColors();
+    method public android.text.TextDirectionHeuristic getTextDirectionHeuristic();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
     method public android.text.PrecomputedText.Params getTextMetricsParams();
@@ -55503,6 +55738,7 @@
     method public boolean isElegantTextHeight();
     method public boolean isFallbackLineSpacing();
     method public boolean isInputMethodTarget();
+    method public boolean isSingleLine();
     method public boolean isSuggestionsEnabled();
     method public boolean isTextSelectable();
     method public int length();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2b41927..a112bba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -17,6 +17,7 @@
     field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
     field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
+    field public static final java.lang.String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final java.lang.String BACKUP = "android.permission.BACKUP";
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -24,7 +25,6 @@
     field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH";
     field public static final java.lang.String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
     field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
-    field public static final java.lang.String BIND_INTELLIGENCE_SERVICE = "android.permission.BIND_INTELLIGENCE_SERVICE";
     field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
     field public static final java.lang.String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
@@ -33,6 +33,7 @@
     field public static final java.lang.String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
     field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
     field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
+    field public static final java.lang.String BIND_SMART_SUGGESTIONS_SERVICE = "android.permission.BIND_SMART_SUGGESTIONS_SERVICE";
     field public static final java.lang.String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
     field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
@@ -107,6 +108,7 @@
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
     field public static final java.lang.String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
+    field public static final java.lang.String MANAGE_SMART_SUGGESTIONS = "android.permission.MANAGE_SMART_SUGGESTIONS";
     field public static final java.lang.String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final java.lang.String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
@@ -158,6 +160,7 @@
     field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
     field public static final java.lang.String REBOOT = "android.permission.REBOOT";
     field public static final java.lang.String RECEIVE_DATA_ACTIVITY_CHANGE = "android.permission.RECEIVE_DATA_ACTIVITY_CHANGE";
+    field public static final java.lang.String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     field public static final java.lang.String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
     field public static final java.lang.String RECOVERY = "android.permission.RECOVERY";
@@ -171,6 +174,7 @@
     field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+    field public static final java.lang.String SEND_DEVICE_CUSTOMIZATION_READY = "android.permission.SEND_DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -214,6 +218,10 @@
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
   }
 
+  public static final class Manifest.permission_group {
+    field public static final java.lang.String UNDEFINED = "android.permission-group.UNDEFINED";
+  }
+
   public static final class R.array {
     field public static final int config_keySystemUuidMapping = 17235973; // 0x1070005
   }
@@ -222,6 +230,7 @@
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+    field public static final int supportsAmbientMode = 16844173; // 0x101058d
     field public static final int userRestriction = 16844164; // 0x1010584
   }
 
@@ -553,6 +562,10 @@
     method public void onVrStateChanged(boolean);
   }
 
+  public final class WallpaperInfo implements android.os.Parcelable {
+    method public boolean supportsAmbientMode();
+  }
+
   public class WallpaperManager {
     method public void clearWallpaper(int, int);
     method public void setDisplayOffset(android.os.IBinder, int, int);
@@ -1055,12 +1068,12 @@
     field public static final java.lang.String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
     field public static final java.lang.String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
     field public static final java.lang.String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
+    field public static final java.lang.String ACTION_DEVICE_CUSTOMIZATION_READY = "android.intent.action.DEVICE_CUSTOMIZATION_READY";
     field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
     field public static final java.lang.String ACTION_GLOBAL_BUTTON = "android.intent.action.GLOBAL_BUTTON";
     field public static final java.lang.String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
     field public static final java.lang.String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
-    field public static final java.lang.String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
     field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
@@ -1232,8 +1245,9 @@
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
     method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method public void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
+    method public deprecated void replacePreferredActivity(android.content.IntentFilter, int, java.util.List<android.content.ComponentName>, android.content.ComponentName);
     method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void sendDeviceCustomizationReadyBroadcast();
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
     method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
@@ -1820,9 +1834,19 @@
     field public static final android.os.Parcelable.Creator<android.hardware.location.ContextHubInfo> CREATOR;
   }
 
+  public class ContextHubIntentEvent {
+    method public static android.hardware.location.ContextHubIntentEvent fromIntent(android.content.Intent);
+    method public android.hardware.location.ContextHubInfo getContextHubInfo();
+    method public int getEventType();
+    method public int getNanoAppAbortCode();
+    method public long getNanoAppId();
+    method public android.hardware.location.NanoAppMessage getNanoAppMessage();
+  }
+
   public final class ContextHubManager {
     method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback, java.util.concurrent.Executor);
     method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.hardware.location.ContextHubClientCallback);
+    method public android.hardware.location.ContextHubClient createClient(android.hardware.location.ContextHubInfo, android.app.PendingIntent, long);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> disableNanoApp(android.hardware.location.ContextHubInfo, long);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> enableNanoApp(android.hardware.location.ContextHubInfo, long);
     method public deprecated int[] findNanoAppOnHub(int, android.hardware.location.NanoAppFilter);
@@ -1839,6 +1863,18 @@
     method public deprecated int unloadNanoApp(int);
     method public android.hardware.location.ContextHubTransaction<java.lang.Void> unloadNanoApp(android.hardware.location.ContextHubInfo, long);
     method public deprecated int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
+    field public static final int EVENT_HUB_RESET = 6; // 0x6
+    field public static final int EVENT_NANOAPP_ABORTED = 4; // 0x4
+    field public static final int EVENT_NANOAPP_DISABLED = 3; // 0x3
+    field public static final int EVENT_NANOAPP_ENABLED = 2; // 0x2
+    field public static final int EVENT_NANOAPP_LOADED = 0; // 0x0
+    field public static final int EVENT_NANOAPP_MESSAGE = 5; // 0x5
+    field public static final int EVENT_NANOAPP_UNLOADED = 1; // 0x1
+    field public static final java.lang.String EXTRA_CONTEXT_HUB_INFO = "android.hardware.location.extra.CONTEXT_HUB_INFO";
+    field public static final java.lang.String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
+    field public static final java.lang.String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
+    field public static final java.lang.String EXTRA_NANOAPP_ABORT_CODE = "android.hardware.location.extra.NANOAPP_ABORT_CODE";
+    field public static final java.lang.String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
   }
 
   public static abstract deprecated class ContextHubManager.Callback {
@@ -2730,7 +2766,6 @@
   public class LocationManager {
     method public deprecated boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
     method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
-    method public android.app.PendingIntent createManageLocationPermissionIntent(java.lang.String, java.lang.String);
     method public void flushGnssBatch();
     method public int getGnssBatchSize();
     method public java.lang.String getNetworkProviderPackage();
@@ -2839,6 +2874,7 @@
     field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+    field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
   public static abstract class AudioManager.AudioServerStateCallback {
@@ -2978,6 +3014,7 @@
 package android.media.session {
 
   public final class MediaSessionManager {
+    method public android.media.session.ISession createSession(android.media.session.MediaSession.CallbackStub, java.lang.String, int);
     method public void setOnMediaKeyListener(android.media.session.MediaSessionManager.OnMediaKeyListener, android.os.Handler);
     method public void setOnVolumeKeyLongPressListener(android.media.session.MediaSessionManager.OnVolumeKeyLongPressListener, android.os.Handler);
   }
@@ -3653,8 +3690,11 @@
 
   public class WifiManager {
     method public void connect(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
+    method public void connect(int, android.net.wifi.WifiManager.ActionListener);
+    method public void disable(int, android.net.wifi.WifiManager.ActionListener);
+    method public void disableEphemeralNetwork(java.lang.String);
+    method public void forget(int, android.net.wifi.WifiManager.ActionListener);
     method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
-    method public java.util.List<android.net.wifi.hotspot2.OsuProvider> getMatchingOsuProviders(java.util.List<android.net.wifi.ScanResult>);
     method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method public int getWifiApState();
@@ -3664,6 +3704,7 @@
     method public boolean isWifiApEnabled();
     method public boolean isWifiScannerSupported();
     method public void registerNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback, android.os.Handler);
+    method public void save(android.net.wifi.WifiConfiguration, android.net.wifi.WifiManager.ActionListener);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
     method public boolean startScan(android.os.WorkSource);
     method public void unregisterNetworkRequestMatchCallback(android.net.wifi.WifiManager.NetworkRequestMatchCallback);
@@ -3812,6 +3853,7 @@
     ctor public WifiScanner.ScanSettings();
     field public int band;
     field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+    field public boolean ignoreLocationSettings;
     field public int maxPeriodInMs;
     field public int maxScansToCache;
     field public int numBssidsPerScan;
@@ -4269,6 +4311,7 @@
 
   public class UserManager {
     method public void clearSeedAccountData();
+    method public android.os.UserHandle getProfileParent(android.os.UserHandle);
     method public java.lang.String getSeedAccountName();
     method public android.os.PersistableBundle getSeedAccountOptions();
     method public java.lang.String getSeedAccountType();
@@ -4588,6 +4631,7 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+    field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = "accessibility_display_magnification_navbar_enabled";
     field public static final java.lang.String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
@@ -4600,6 +4644,8 @@
     field public static final java.lang.String HUSH_GESTURE_USED = "hush_gesture_used";
     field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
     field public static final java.lang.String LAST_SETUP_SHOWN = "last_setup_shown";
+    field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+    field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
     field public static final java.lang.String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
     field public static final java.lang.String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
     field public static final java.lang.String MANUAL_RINGER_TOGGLE_COUNT = "manual_ringer_toggle_count";
@@ -4937,6 +4983,13 @@
 
 package android.service.intelligence {
 
+  public final class ContentCaptureEventsRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.view.intelligence.ContentCaptureEvent> getEvents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.intelligence.ContentCaptureEventsRequest> CREATOR;
+  }
+
   public final class FillCallback {
     method public void onSuccess(android.service.intelligence.FillResponse);
   }
@@ -4971,16 +5024,6 @@
     field public static final long FLAG_METADATA_ADDRESS = 1L; // 0x1L
   }
 
-  public abstract class IntelligenceService extends android.app.Service {
-    ctor public IntelligenceService();
-    method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
-    method public abstract void onContentCaptureEvent(android.service.intelligence.InteractionSessionId, java.util.List<android.view.intelligence.ContentCaptureEvent>);
-    method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
-    method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
-    method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
-    field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.IntelligenceService";
-  }
-
   public final class InteractionContext implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
@@ -5016,6 +5059,21 @@
     method public android.service.intelligence.PresentationParams.Area getSubArea(android.graphics.Rect);
   }
 
+  public abstract class SmartSuggestionsService extends android.app.Service {
+    ctor public SmartSuggestionsService();
+    method public final java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
+    method public final java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
+    method public void onActivitySnapshot(android.service.intelligence.InteractionSessionId, android.service.intelligence.SnapshotData);
+    method public abstract void onContentCaptureEventsRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.ContentCaptureEventsRequest);
+    method public void onCreateInteractionSession(android.service.intelligence.InteractionContext, android.service.intelligence.InteractionSessionId);
+    method public void onDestroyInteractionSession(android.service.intelligence.InteractionSessionId);
+    method public void onFillRequest(android.service.intelligence.InteractionSessionId, android.service.intelligence.FillRequest, android.os.CancellationSignal, android.service.intelligence.FillController, android.service.intelligence.FillCallback);
+    method public final void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
+    method public final void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
+    method public final void setPackageContentCaptureEnabled(java.lang.String, boolean);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.intelligence.SmartSuggestionsService";
+  }
+
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.assist.AssistContent getAssistContent();
@@ -5074,6 +5132,7 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
@@ -5137,6 +5196,7 @@
 package android.service.oemlock {
 
   public class OemLockManager {
+    method public java.lang.String getLockName();
     method public boolean isOemUnlockAllowedByCarrier();
     method public boolean isOemUnlockAllowedByUser();
     method public void setOemUnlockAllowedByCarrier(boolean, byte[]);
@@ -5240,6 +5300,16 @@
 
 }
 
+package android.service.sms {
+
+  public abstract class FinancialSmsService extends android.app.Service {
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.database.CursorWindow onGetSmsMessages(android.os.Bundle);
+    field public static final java.lang.String ACTION_FINANCIAL_SERVICE_INTENT = "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+  }
+
+}
+
 package android.service.textclassifier {
 
   public abstract class TextClassifierService extends android.app.Service {
@@ -5297,6 +5367,15 @@
 
 }
 
+package android.service.wallpaper {
+
+  public class WallpaperService.Engine {
+    method public boolean isInAmbientMode();
+    method public void onAmbientModeChanged(boolean, long);
+  }
+
+}
+
 package android.telecom {
 
   public deprecated class AudioState implements android.os.Parcelable {
@@ -5635,6 +5714,26 @@
     field public static final int RESULT_SUCCESS = 0; // 0x0
   }
 
+  public abstract interface NumberVerificationCallback {
+    method public default void onCallReceived(java.lang.String);
+    method public default void onVerificationFailed(int);
+    field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
+    field public static final int REASON_IN_ECBM = 5; // 0x5
+    field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
+    field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
+    field public static final int REASON_TIMED_OUT = 1; // 0x1
+    field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
+    field public static final int REASON_UNSPECIFIED = 0; // 0x0
+  }
+
+  public final class PhoneNumberRange implements android.os.Parcelable {
+    ctor public PhoneNumberRange(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public boolean matches(java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
+  }
+
   public class PhoneStateListener {
     method public void onRadioPowerStateChanged(int);
     method public void onSrvccStateChanged(int);
@@ -5799,6 +5898,7 @@
     method public boolean needsOtaServiceProvisioning();
     method public boolean rebootRadio();
     method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
+    method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback);
     method public boolean resetRadioConfig();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
     method public void setCarrierDataEnabled(boolean);
@@ -5828,6 +5928,7 @@
     field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+    field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
     field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
     field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
     field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
@@ -5866,6 +5967,7 @@
     field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
     field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
     field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
+    field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000
     field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
     field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
     field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
@@ -6178,6 +6280,7 @@
     method public android.os.Bundle getCallExtras();
     method public int getCallType();
     method public static int getCallTypeFromVideoState(int);
+    method public int getEmergencyServiceCategories();
     method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
     method public int getRestrictCause();
     method public int getServiceType();
@@ -6190,6 +6293,7 @@
     method public void setCallExtraBoolean(java.lang.String, boolean);
     method public void setCallExtraInt(java.lang.String, int);
     method public void setCallRestrictCause(int);
+    method public void setEmergencyServiceCategories(int);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -7176,14 +7280,6 @@
     field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
   }
 
-  public final class IntelligenceManager {
-    method public java.util.Set<android.content.ComponentName> getContentCaptureDisabledActivities();
-    method public java.util.Set<java.lang.String> getContentCaptureDisabledPackages();
-    method public void setActivityContentCaptureEnabled(android.content.ComponentName, boolean);
-    method public void setContentCaptureWhitelist(java.util.List<java.lang.String>, java.util.List<android.content.ComponentName>);
-    method public void setPackageContentCaptureEnabled(java.lang.String, boolean);
-  }
-
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
     method public android.view.autofill.AutofillId getParentAutofillId();
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 738caec..46cbb52 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -61,6 +61,7 @@
   }
 
   public class ActivityTaskManager {
+    method public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
     method public java.lang.String listAllStacks();
     method public void moveTaskToStack(int, int, boolean);
     method public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
@@ -81,6 +82,10 @@
     field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
   }
 
+  public class AppDetailsActivity extends android.app.Activity {
+    ctor public AppDetailsActivity();
+  }
+
   public class AppOpsManager {
     method public java.util.List<android.app.AppOpsManager.HistoricalPackageOps> getAllHistoricPackagesOps(java.lang.String[], long, long);
     method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
@@ -300,16 +305,11 @@
 
   public abstract class Context {
     method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public abstract java.lang.String getOpPackageName();
     method public android.os.UserHandle getUser();
     method public int getUserId();
     method public void setAutofillCompatibilityEnabled(boolean);
   }
 
-  public class ContextWrapper extends android.content.Context {
-    method public java.lang.String getOpPackageName();
-  }
-
 }
 
 package android.content.pm {
@@ -943,6 +943,10 @@
 
 package android.os.storage {
 
+  public class StorageManager {
+    method public static boolean hasIsolatedStorage();
+  }
+
   public final class StorageVolume implements android.os.Parcelable {
     method public java.lang.String getPath();
   }
@@ -998,6 +1002,7 @@
   }
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
+    method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
     field public static final java.lang.String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -1009,6 +1014,8 @@
     field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
     field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
     field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
+    field public static final java.lang.String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
+    field public static final java.lang.String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
     field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final java.lang.String USER_SETUP_COMPLETE = "user_setup_complete";
     field public static final java.lang.String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
@@ -1155,6 +1162,7 @@
     ctor public NotificationAssistantService();
     method public final void adjustNotification(android.service.notification.Adjustment);
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
+    method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReply(java.lang.String);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 1597c8c..52a2ab4 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -77,7 +77,7 @@
                     + "  <BINDING> binds a typed value to a column and is formatted:\n"
                     + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
                     + "  <TYPE> specifies data type such as:\n"
-                    + "  b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+                    + "  b - boolean, s - string, i - integer, l - long, f - float, d - double, n - null\n"
                     + "  Note: Omit the value for passing an empty string, e.g column:s:\n"
                     + "  Example:\n"
                     + "  # Add \"new_setting\" secure setting with value \"new_value\".\n"
@@ -153,6 +153,7 @@
         private static final String TYPE_LONG = "l";
         private static final String TYPE_FLOAT = "f";
         private static final String TYPE_DOUBLE = "d";
+        private static final String TYPE_NULL = "n";
         private static final String COLON = ":";
         private static final String ARGUMENT_PREFIX = "--";
 
@@ -410,6 +411,8 @@
                 values.put(column, Long.parseLong(value));
             } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
                 values.put(column, Double.parseDouble(value));
+            } else if (TYPE_NULL.equalsIgnoreCase(type)) {
+                values.putNull(column);
             } else {
                 throw new IllegalArgumentException("Unsupported type: " + type);
             }
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 7d675ce..d7922bc 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -181,6 +181,7 @@
         "libutils",
         "libziparchive",
     ],
+    init_rc: ["idmap2d/idmap2d.rc"],
 }
 
 filegroup {
diff --git a/cmds/idmap2/idmap2d/idmap2d.rc b/cmds/idmap2/idmap2d/idmap2d.rc
new file mode 100644
index 0000000..203e7be
--- /dev/null
+++ b/cmds/idmap2/idmap2d/idmap2d.rc
@@ -0,0 +1,4 @@
+service idmap2d /system/bin/idmap2d
+    class main
+    user system
+    group system
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 1c3ebd8..b23c1ed 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -24,6 +24,7 @@
 #include "incidentd_util.h"
 #include "section_list.h"
 
+#include <android/os/IncidentReportArgs.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IResultReceiver.h>
 #include <binder/IServiceManager.h>
@@ -41,6 +42,15 @@
 #define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024)        // 20MB
 #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000)  // 1 Day
 
+// Skip logs (1100 - 1108) because they are already in the bug report
+// Skip 1200, 1201, 1202, 3018 because they take too long
+// TODO(120079956): Skip 3008, 3015 because of error
+#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
+                           1200, 1201, 1202, /* Native, hal, java traces */ \
+                           3008, /* "package --proto" */ \
+                           3015, /* "activity --proto processes" */ \
+                           3018  /* "meminfo -a --proto" */ }
+
 namespace android {
 namespace os {
 namespace incidentd {
@@ -391,6 +401,38 @@
     return NO_ERROR;
 }
 
+status_t IncidentService::dump(int fd, const Vector<String16>& args) {
+    if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
+        ALOGD("Skip dumping incident. Only proto format is supported.");
+        dprintf(fd, "Incident dump only supports proto version.\n");
+        return NO_ERROR;
+    }
+
+    ALOGD("Dump incident proto");
+    IncidentReportArgs incidentArgs;
+    incidentArgs.setDest(DEST_EXPLICIT);
+    int skipped[] = SKIPPED_SECTIONS;
+    for (const Section** section = SECTION_LIST; *section; section++) {
+        const int id = (*section)->id;
+        if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)) {
+            incidentArgs.addSection(id);
+        }
+    }
+
+    if (!checkIncidentPermissions(incidentArgs).isOk()) {
+        return PERMISSION_DENIED;
+    }
+
+    int fd1 = dup(fd);
+    if (fd1 < 0) {
+        return -errno;
+    }
+
+    mHandler->scheduleRunReport(new ReportRequest(incidentArgs, NULL, fd1));
+
+    return NO_ERROR;
+}
+
 }  // namespace incidentd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index e176bfd..6252ad2 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -112,6 +112,7 @@
     virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
                                 uint32_t flags) override;
     virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
+    virtual status_t dump(int fd, const Vector<String16>& args);
 
 private:
     sp<ReportRequestQueue> mQueue;
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
index 4948823..098d74e 100644
--- a/cmds/incidentd/src/main.cpp
+++ b/cmds/incidentd/src/main.cpp
@@ -45,7 +45,8 @@
 
     // Create the service
     sp<IncidentService> service = new IncidentService(looper);
-    if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+    if (defaultServiceManager()->addService(String16("incident"), service, false,
+            IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
         ALOGE("Failed to add service");
         return -1;
     }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ada5d4c..41a2021 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -34,6 +34,8 @@
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
 import "frameworks/base/core/proto/android/telephony/enums.proto";
 import "frameworks/base/core/proto/android/view/enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto";
+import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -156,6 +158,7 @@
         ServiceLaunchReported service_launch_reported = 100;
         PhenotypeFlagStateChanged phenotype_flag_state_changed = 101;
         BinaryPushStateChanged binary_push_state_changed = 102;
+        DevicePolicyEvent device_policy_event = 103;
     }
 
     // Pulled events will start at field 10000.
@@ -205,6 +208,7 @@
         DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
         ProcessMemoryHighWaterMark process_memory_high_water_mark = 10042;
         BatteryLevel battery_level = 10043;
+        BuildInformation build_information = 10044;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -1334,8 +1338,9 @@
  * Log bucketed battery charge cycles.
  *
  * Each bucket represents cycles of the battery past
- * a given charge point.  For example, bucket 1 is the
- * lowest 1/8th of the battery, and bucket 8 is 100%.
+ * a given charge point.  For example, if 10 cycle buckets are
+ * initialized, bucket 1 is the lowest 1/10th of the battery,
+ * and bucket 10 is 100%.
  *
  * Logged from:
  * /sys/class/power_supply/bms/cycle_count, via Vendor.
@@ -1349,6 +1354,8 @@
     optional int32 cycle_bucket_6 = 6;
     optional int32 cycle_bucket_7 = 7;
     optional int32 cycle_bucket_8 = 8;
+    optional int32 cycle_bucket_9 = 9;
+    optional int32 cycle_bucket_10 = 10;
 }
 
 /**
@@ -2011,6 +2018,9 @@
 
     // SWAP
     optional int64 swap_in_bytes = 8;
+
+    // The elapsed real time of start of the process.
+    optional int64 process_start_time_nanos = 9;
 }
 
 /*
@@ -2536,10 +2546,7 @@
     // SWAP
     optional int64 swap_in_bytes = 8;
 
-    // RSS high watermark.
-    // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
-    // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
-    // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+    // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
     optional int64 rss_high_watermark_in_bytes = 9 [deprecated = true];
 
     // Elapsed real time when the process started.
@@ -2566,9 +2573,7 @@
     // RSS
     optional int64 rss_in_bytes = 5;
 
-    // RSS high watermark.
-    // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status.
-    // Deprecated: use ProcessMemoryHighWaterMark atom instead.
+    // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
     optional int64 rss_high_watermark_in_bytes = 6 [deprecated = true];
 
     // Elapsed real time when the process started.
@@ -3324,10 +3329,61 @@
     optional string process_name = 4;
     // Name of the thread taken from `/proc/$PID/task/$TID/comm`
     optional string thread_name = 5;
-    // What frequency the CPU was running at, in KHz
-    optional int32 frequency_khz = 6;
-    // Time spent in frequency in milliseconds, since thread start.
-    optional int32 time_millis = 7;
+
+    // Report eight different frequencies, and how much time is spent in each frequency. Frequencies
+    // are given in KHz, and time is given in milliseconds since the thread started. All eight
+    // frequencies are given here as the alternative is sending eight separate atoms. This method
+    // significantly reduces the amount of data created
+    optional int32 frequency1_khz = 6;
+    optional int32 time1_millis = 7;
+    optional int32 frequency2_khz = 8;
+    optional int32 time2_millis = 9;
+    optional int32 frequency3_khz = 10;
+    optional int32 time3_millis = 11;
+    optional int32 frequency4_khz = 12;
+    optional int32 time4_millis = 13;
+    optional int32 frequency5_khz = 14;
+    optional int32 time5_millis = 15;
+    optional int32 frequency6_khz = 16;
+    optional int32 time6_millis = 17;
+    optional int32 frequency7_khz = 18;
+    optional int32 time7_millis = 19;
+    optional int32 frequency8_khz = 20;
+    optional int32 time8_millis = 21;
+}
+
+/**
+ * Pulls information about the device's build.
+ */
+message BuildInformation {
+    // Build.FINGERPRINT. A string that uniquely identifies this build. Do not parse.
+    // E.g. may be composed of the brand, product, device, release, id, incremental, type, and tags.
+    optional string fingerprint = 1;
+
+    // Build.BRAND. The consumer-visible brand with which the product/hardware will be associated.
+    optional string brand = 2;
+
+    // Build.PRODUCT. The name of the overall product.
+    optional string product = 3;
+
+    // Build.DEVICE. The name of the industrial design.
+    optional string device = 4;
+
+    // Build.VERSION.RELEASE. The user-visible version string.  E.g., "1.0" or "3.4b5" or "bananas".
+    optional string version_release = 5;
+
+    // Build.ID. E.g. a label like "M4-rc20".
+    optional string id = 6;
+
+    // Build.VERSION.INCREMENTAL. The internal value used by the underlying source control to
+    // represent this build.
+    optional string version_incremental = 7;
+
+    // Build.TYPE. The type of build, like "user" or "eng".
+    optional string type = 8;
+
+    // Build.TAGS. Comma-separated tags describing the build, like "unsigned,debug".
+    optional string tags = 9;
 }
 
 /**
@@ -3389,3 +3445,25 @@
     // (i.e. roughly since device was last significantly charged).
     optional float power_milli_amp_hours = 2;
 }
+
+/**
+ * Logs device policy features.
+ *
+ * Logged from:
+ *   frameworks/base/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+ *   packages/apps/ManagedProvisioning/src/com/android/managedprovisioning/
+ */
+message DevicePolicyEvent {
+    // The event id - unique for each event.
+    optional android.stats.devicepolicy.EventId event_id = 1;
+    // The admin package name.
+    optional string admin_package_name = 2;
+    // A generic integer parameter.
+    optional int32 integer_value = 3;
+    // A generic boolean parameter.
+    optional bool boolean_value = 4;
+    // A parameter specifying a time period in milliseconds.
+    optional uint64 time_period_millis = 5;
+    // A parameter specifying a list of package names, bundle extras or string parameters.
+    optional android.stats.devicepolicy.StringList string_list_value = 6 [(log_mode) = MODE_BYTES];
+}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 3eb05a9..4e4b8f3 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -59,7 +59,7 @@
         }
         data->clear();
         for (const StatsLogEventWrapper& it : returned_value) {
-            data->push_back(make_shared<LogEvent>(it));
+            LogEvent::createLogEvents(it, *data);
         }
         VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
         return true;
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index e3f251a..f501574 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -32,9 +32,10 @@
 sp<UidMap> StatsPuller::mUidMap = nullptr;
 void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
 
-// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
+    // Pullers can cause significant impact to system health and battery.
+    // So that we don't pull too frequently.
     mCoolDownNs = StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
     VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
@@ -64,8 +65,8 @@
         data->setLogdWallClockTimestampNs(wallClockTimeNs);
     }
     if (ret && mCachedData.size() > 0) {
-      mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
-      (*data) = mCachedData;
+        mapAndMergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+        (*data) = mCachedData;
     }
     StatsdStats::getInstance().notePullDelay(mTagId, getElapsedRealtimeNs() - elapsedTimeNs);
     return ret;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index ab635a0..c070ca3 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -54,52 +54,42 @@
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
          {{2, 3, 4, 5},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
         // wifi_bytes_transfer_by_fg_bg
         {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
          {{3, 4, 5, 6},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
         // mobile_bytes_transfer
         {android::util::MOBILE_BYTES_TRANSFER,
          {{2, 3, 4, 5},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
         // mobile_bytes_transfer_by_fg_bg
         {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
          {{3, 4, 5, 6},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
         // bluetooth_bytes_transfer
         {android::util::BLUETOOTH_BYTES_TRANSFER,
          {{2, 3},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
         // kernel_wakelock
         {android::util::KERNEL_WAKELOCK,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
         // subsystem_sleep_state
         {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
+         {{}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
         // on_device_power_measurement
-        {android::util::ON_DEVICE_POWER_MEASUREMENT,
-         {{}, {}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
+        {android::util::ON_DEVICE_POWER_MEASUREMENT, {{}, 1 * NS_PER_SEC, new PowerStatsPuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
         // cpu_time_per_uid
         {android::util::CPU_TIME_PER_UID,
          {{2, 3},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
         // cpu_time_per_uid_freq
@@ -107,164 +97,140 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_TIME_PER_UID_FREQ,
          {{4},
-          {2, 3},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
         // cpu_active_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_ACTIVE_TIME,
-         {{2},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+         {{2}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
         // cpu_cluster_time
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_CLUSTER_TIME,
-         {{3},
-          {2},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+         {{3}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
         {android::util::WIFI_ACTIVITY_INFO,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
         // bluetooth_activity_info
         {android::util::BLUETOOTH_ACTIVITY_INFO,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
         // system_elapsed_realtime
         {android::util::SYSTEM_ELAPSED_REALTIME,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
         // system_uptime
         {android::util::SYSTEM_UPTIME,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
         // remaining_battery_capacity
         {android::util::REMAINING_BATTERY_CAPACITY,
          {{},
-          {},
           1 * NS_PER_SEC,
           new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
         // full_battery_capacity
         {android::util::FULL_BATTERY_CAPACITY,
          {{},
-          {},
           1 * NS_PER_SEC,
           new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // battery_voltage
         {android::util::BATTERY_VOLTAGE,
-         {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
-         // battery_voltage
+         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+        // battery_level
         {android::util::BATTERY_LEVEL,
-         {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
+         {{}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_LEVEL)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
          {{4, 5, 6, 7, 8, 9},
-          {2, 3, 10},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // native_process_memory_state
         {android::util::NATIVE_PROCESS_MEMORY_STATE,
          {{3, 4, 5, 6},
-          {2, 7},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::NATIVE_PROCESS_MEMORY_STATE)}},
         {android::util::PROCESS_MEMORY_HIGH_WATER_MARK,
          {{3},
-          {2},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
         // temperature
-        {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
+        {android::util::TEMPERATURE, {{}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}},
         // binder_calls
         {android::util::BINDER_CALLS,
          {{4, 5, 6, 8, 12},
-          {2, 3, 7, 9, 10, 11, 13},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BINDER_CALLS)}},
         // binder_calls_exceptions
         {android::util::BINDER_CALLS_EXCEPTIONS,
          {{},
-          {},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
         // looper_stats
         {android::util::LOOPER_STATS,
          {{5, 6, 7, 8, 9},
-          {2, 3, 4, 10},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
         // Disk Stats
         {android::util::DISK_STATS,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_STATS)}},
         // Directory usage
         {android::util::DIRECTORY_USAGE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
         // Size of app's code, data, and cache
         {android::util::APP_SIZE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
         // Size of specific categories of files. Eg. Music.
         {android::util::CATEGORY_SIZE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
         // Number of fingerprints registered to each user.
         {android::util::NUM_FINGERPRINTS,
-         {{},
-          {},
-          1 * NS_PER_SEC,
-          new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
         // ProcStats.
         {android::util::PROC_STATS,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
         // ProcStatsPkgProc.
         {android::util::PROC_STATS_PKG_PROC,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
         // Disk I/O stats per uid.
         {android::util::DISK_IO,
-         {{2,3,4,5,6,7,8,9,10,11},
-          {},
+         {{2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
           3 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DISK_IO)}},
         // PowerProfile constants for power model calculations.
         {android::util::POWER_PROFILE,
-         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
         // Process cpu stats. Min cool-down is 5 sec, inline with what AcitivityManagerService uses.
         {android::util::PROCESS_CPU_TIME,
-            {{} /* additive fields */, {} /* non additive fields */,
-             5 * NS_PER_SEC /* min cool-down in seconds*/,
-             new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
+         {{} /* additive fields */,
+          5 * NS_PER_SEC /* min cool-down in seconds*/,
+          new StatsCompanionServicePuller(android::util::PROCESS_CPU_TIME)}},
         {android::util::CPU_TIME_PER_THREAD_FREQ,
-         {{7},
-          {2, 3, 4, 5, 6},
+         {{7, 9, 11, 13, 15, 17, 19, 21},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
         // DeviceCalculatedPowerUse.
         {android::util::DEVICE_CALCULATED_POWER_USE,
-         {{}, {}, 1 * NS_PER_SEC,
+         {{},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
         // DeviceCalculatedPowerBlameUid.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
-         {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+         {{},  // BatteryStats already merged isolated with host ids so it's unnecessary here.
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
         // DeviceCalculatedPowerBlameOther.
         {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
-         {{}, {},
+         {{},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
+        // BuildInformation.
+        {android::util::BUILD_INFORMATION,
+         {{}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::BUILD_INFORMATION)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index bbf5d9d..3350736 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -36,9 +36,6 @@
     // The field numbers of the fields that need to be summed when merging
     // isolated uid with host uid.
     std::vector<int> additiveFields;
-    // The field numbers of the fields that can't be merged when merging
-    // data belong to isolated uid and host uid.
-    std::vector<int> nonAdditiveFields;
     // How long should the puller wait before doing an actual pull again. Default
     // 1 sec. Set this to 0 if this is handled elsewhere.
     int64_t coolDownNs = 1 * NS_PER_SEC;
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
index ea7fa97..0b9b6ab 100644
--- a/cmds/statsd/src/external/puller_util.cpp
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -25,67 +25,13 @@
 namespace os {
 namespace statsd {
 
+using std::list;
 using std::map;
+using std::set;
 using std::shared_ptr;
+using std::sort;
 using std::vector;
 
-namespace {
-bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                 const vector<int>& nonAdditiveFields) {
-    const auto& l_values = lhs->getValues();
-    const auto& r_values = rhs->getValues();
-
-    for (size_t i : nonAdditiveFields) {
-        // We store everything starting from index 0, so we need to use i-1
-        if (!(l_values.size() > i - 1 && r_values.size() > i - 1 &&
-              l_values[i - 1].mValue == r_values[i - 1].mValue)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-// merge rhs to lhs
-// when calling this function, all sanity check should be done already.
-// e.g., index boundary, nonAdditiveFields matching etc.
-bool mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
-                const vector<int>& additiveFields) {
-    vector<FieldValue>* host_values = lhs->getMutableValues();
-    const auto& child_values = rhs->getValues();
-    for (int i : additiveFields) {
-        Value& host = (*host_values)[i - 1].mValue;
-        const Value& child = (child_values[i - 1]).mValue;
-        if (child.getType() != host.getType()) {
-            return false;
-        }
-        switch (child.getType()) {
-            case INT:
-                host.setInt(host.int_value + child.int_value);
-                break;
-            case LONG:
-                host.setLong(host.long_value + child.long_value);
-                break;
-            default:
-                ALOGE("Tried to merge 2 fields with unsupported type");
-                return false;
-        }
-    }
-    return true;
-}
-
-bool tryMerge(vector<shared_ptr<LogEvent>>& data, int child_pos, const vector<int>& host_pos,
-              const vector<int>& nonAdditiveFields, const vector<int>& additiveFields) {
-    for (const auto& pos : host_pos) {
-        if (shouldMerge(data[pos], data[child_pos], nonAdditiveFields) &&
-            mergeEvent(data[pos], data[child_pos], additiveFields)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-}  // namespace
-
 /**
  * Process all data and merge isolated with host if necessary.
  * For example:
@@ -95,7 +41,7 @@
  *       int byte_send = 3;
  *       int byte_recv = 4;
  *   }
- *   additive fields are {3, 4}, non-additive field is {2}
+ *   additive fields are {3, 4}
  * If we pulled the following events (uid1_child is an isolated uid which maps to uid1):
  * [uid1, fg, 100, 200]
  * [uid1_child, fg, 100, 200]
@@ -104,65 +50,119 @@
  * We want to merge them and results should be:
  * [uid1, fg, 200, 400]
  * [uid1, bg, 100, 200]
+ *
+ * All atoms should be of the same tagId. All fields should be present.
  */
-void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
-                                int tagId) {
+void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap,
+                                      int tagId) {
     if (StatsPullerManager::kAllPullAtomInfo.find(tagId) ==
         StatsPullerManager::kAllPullAtomInfo.end()) {
         VLOG("Unknown pull atom id %d", tagId);
         return;
     }
-    int uidField;
-    auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
-    if (it == android::util::AtomsInfo::kAtomsWithUidField.end()) {
-        VLOG("No uid to merge for atom %d", tagId);
+    if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) ==
+         android::util::AtomsInfo::kAtomsWithAttributionChain.end()) &&
+        (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) ==
+         android::util::AtomsInfo::kAtomsWithUidField.end())) {
+        VLOG("No uid or attribution chain to merge, atom %d", tagId);
         return;
-    } else {
-        uidField = it->second;  // uidField is the field number in proto,
     }
-    const vector<int>& additiveFields =
-            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
-    const vector<int>& nonAdditiveFields =
-            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.nonAdditiveFields;
 
-    // map of host uid to their position in the original vector
-    map<int, vector<int>> hostPosition;
-    vector<bool> toRemove = vector<bool>(data.size(), false);
-
-    for (size_t i = 0; i < data.size(); i++) {
-        vector<FieldValue>* valueList = data[i]->getMutableValues();
-
-        int uid;
-        if (uidField > 0 && (int)data[i]->getValues().size() >= uidField &&
-            (data[i]->getValues())[uidField - 1].mValue.getType() == INT) {
-            uid = (*data[i]->getMutableValues())[uidField - 1].mValue.int_value;
-        } else {
-            ALOGE("Malformed log, uid not found. %s", data[i]->ToString().c_str());
-            continue;
+    // 1. Map all isolated uid in-place to host uid
+    for (shared_ptr<LogEvent>& event : data) {
+        if (event->GetTagId() != tagId) {
+            ALOGE("Wrong atom. Expecting %d, got %d", tagId, event->GetTagId());
+            return;
         }
-
-        const int hostUid = uidMap->getHostUidOrSelf(uid);
-
-        if (hostUid != uid) {
-            (*valueList)[0].mValue.setInt(hostUid);
-        }
-        if (hostPosition.find(hostUid) == hostPosition.end()) {
-            hostPosition[hostUid].push_back(i);
+        if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) !=
+            android::util::AtomsInfo::kAtomsWithAttributionChain.end()) {
+            for (auto& value : *(event->getMutableValues())) {
+                if (value.mField.getPosAtDepth(0) > kAttributionField) {
+                    break;
+                }
+                if (isAttributionUidField(value)) {
+                    const int hostUid = uidMap->getHostUidOrSelf(value.mValue.int_value);
+                    value.mValue.setInt(hostUid);
+                }
+            }
         } else {
-            if (tryMerge(data, i, hostPosition[hostUid], nonAdditiveFields, additiveFields)) {
-                toRemove[i] = true;
-            } else {
-                hostPosition[hostUid].push_back(i);
+            auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId);
+            if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+                int uidField = it->second;  // uidField is the field number in proto,
+                // starting from 1
+                if (uidField > 0 && (int)event->getValues().size() >= uidField &&
+                    (event->getValues())[uidField - 1].mValue.getType() == INT) {
+                    Value& value = (*event->getMutableValues())[uidField - 1].mValue;
+                    const int hostUid = uidMap->getHostUidOrSelf(value.int_value);
+                    value.setInt(hostUid);
+                } else {
+                    ALOGE("Malformed log, uid not found. %s", event->ToString().c_str());
+                    return;
+                }
             }
         }
     }
 
+    // 2. sort the data, bit-wise
+    sort(data.begin(), data.end(),
+         [](const shared_ptr<LogEvent>& lhs, const shared_ptr<LogEvent>& rhs) {
+             if (lhs->size() != rhs->size()) {
+                 return lhs->size() < rhs->size();
+             }
+             const std::vector<FieldValue>& lhsValues = lhs->getValues();
+             const std::vector<FieldValue>& rhsValues = rhs->getValues();
+             for (int i = 0; i < (int)lhs->size(); i++) {
+                 if (lhsValues[i] != rhsValues[i]) {
+                     return lhsValues[i] < rhsValues[i];
+                 }
+             }
+             return false;
+         });
+
     vector<shared_ptr<LogEvent>> mergedData;
-    for (size_t i = 0; i < toRemove.size(); i++) {
-        if (!toRemove[i]) {
+    const vector<int>& additiveFieldsVec =
+            StatsPullerManager::kAllPullAtomInfo.find(tagId)->second.additiveFields;
+    const set<int> additiveFields(additiveFieldsVec.begin(), additiveFieldsVec.end());
+    bool needMerge = true;
+
+    // 3. do the merge.
+    // The loop invariant is this: for every event, check if it differs on
+    // non-additive fields, or have different attribution chain length.
+    // If so, no need to merge, add itself to the result.
+    // Otherwise, merge the value onto the one immediately next to it.
+    for (int i = 0; i < (int)data.size() - 1; i++) {
+        // Size different, must be different chains.
+        if (data[i]->size() != data[i + 1]->size()) {
             mergedData.push_back(data[i]);
+            continue;
+        }
+        vector<FieldValue>* lhsValues = data[i]->getMutableValues();
+        vector<FieldValue>* rhsValues = data[i + 1]->getMutableValues();
+        needMerge = true;
+        for (int p = 0; p < (int)lhsValues->size(); p++) {
+            if ((*lhsValues)[p] != (*rhsValues)[p]) {
+                int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+                // Differ on non-additive field, abort.
+                if (additiveFields.find(pos) == additiveFields.end()) {
+                    needMerge = false;
+                    break;
+                }
+            }
+        }
+        if (!needMerge) {
+            mergedData.push_back(data[i]);
+            continue;
+        }
+        // This should be infrequent operation.
+        for (int p = 0; p < (int)lhsValues->size(); p++) {
+            int pos = (*lhsValues)[p].mField.getPosAtDepth(0);
+            if (additiveFields.find(pos) != additiveFields.end()) {
+                (*rhsValues)[p].mValue += (*lhsValues)[p].mValue;
+            }
         }
     }
+    mergedData.push_back(data.back());
+
     data.clear();
     data = mergedData;
 }
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
index fd4a4a2..f703e6c 100644
--- a/cmds/statsd/src/external/puller_util.h
+++ b/cmds/statsd/src/external/puller_util.h
@@ -25,8 +25,8 @@
 namespace os {
 namespace statsd {
 
-void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
-                                const sp<UidMap>& uidMap, int tagId);
+void mapAndMergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+                                      const sp<UidMap>& uidMap, int tagId);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 6617689..3e5e82f 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -101,6 +101,7 @@
 
 const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
         {android::util::BINDER_CALLS, {6000, 10000}},
+        {android::util::LOOPER_STATS, {1500, 2500}},
         {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
 };
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 625294c..8d61aba 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -41,13 +41,28 @@
     }
 }
 
-LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper) {
+LogEvent::LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex) {
     mTagId = statsLogEventWrapper.getTagId();
     mLogdTimestampNs = statsLogEventWrapper.getWallClockTimeNs();
     mElapsedTimestampNs = statsLogEventWrapper.getElapsedRealTimeNs();
     mLogUid = 0;
+    int workChainPosOffset = 0;
+    if (workChainIndex != -1) {
+        const WorkChain& wc = statsLogEventWrapper.getWorkChains()[workChainIndex];
+        // chains are at field 1, level 2
+        int depth = 2;
+        for (int i = 0; i < (int)wc.uids.size(); i++) {
+            int pos[] = {1, i + 1, 1};
+            mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.uids[i])));
+            pos[2]++;
+            mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(wc.tags[i])));
+            mValues.back().mField.decorateLastPos(2);
+        }
+        mValues.back().mField.decorateLastPos(1);
+        workChainPosOffset = 1;
+    }
     for (int i = 0; i < (int)statsLogEventWrapper.getElements().size(); i++) {
-        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1));
+        Field field(statsLogEventWrapper.getTagId(), getSimpleField(i + 1 + workChainPosOffset));
         switch (statsLogEventWrapper.getElements()[i].type) {
             case android::os::StatsLogValue::STATS_LOG_VALUE_TYPE::INT:
                 mValues.push_back(
@@ -79,6 +94,17 @@
     }
 }
 
+void LogEvent::createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+                               std::vector<std::shared_ptr<LogEvent>>& logEvents) {
+    if (statsLogEventWrapper.getWorkChains().size() == 0) {
+        logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, -1));
+    } else {
+        for (size_t i = 0; i < statsLogEventWrapper.getWorkChains().size(); i++) {
+            logEvents.push_back(std::make_shared<LogEvent>(statsLogEventWrapper, i));
+        }
+    }
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
     mLogdTimestampNs = wallClockTimestampNs;
     mTagId = tagId;
@@ -653,7 +679,7 @@
 
 string LogEvent::ToString() const {
     string result;
-    result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs,
+    result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
                            (long long)mElapsedTimestampNs, mTagId);
     for (const auto& value : mValues) {
         result +=
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 3d5b2ab..5408d17 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -65,7 +65,16 @@
      */
     explicit LogEvent(log_msg& msg);
 
-    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper);
+    /**
+     * Creates LogEvent from StatsLogEventWrapper.
+     */
+    static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
+                                std::vector<std::shared_ptr<LogEvent>>& logEvents);
+
+    /**
+     * Construct one LogEvent from a StatsLogEventWrapper with the i-th work chain. -1 if no chain.
+     */
+    explicit LogEvent(const StatsLogEventWrapper& statsLogEventWrapper, int workChainIndex);
 
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c3912ee..9fe84dc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -165,7 +165,7 @@
     const bool mSkipZeroDiffOutput;
 
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
-  FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
+    FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
     FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4ac55b5..b317361 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -454,6 +454,16 @@
             ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
             return false;
         }
+        if (!metric.has_value_field()) {
+            ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+            return false;
+        }
+        std::vector<Matcher> fieldMatchers;
+        translateFieldMatcher(metric.value_field(), &fieldMatchers);
+        if (fieldMatchers.size() < 1) {
+            ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+            return false;
+        }
 
         int metricIndex = allMetricProducers.size();
         metricMap.insert({metric.id(), metricIndex});
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 504c586..f1310db 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -343,9 +343,11 @@
                         }
                     }
                     if (isBytesField) {
-                        protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
-                                           (const char*)dim.mValue.str_value.c_str(),
-                                           dim.mValue.str_value.length());
+                        if (dim.mValue.str_value.length() > 0) {
+                            protoOutput->write(FIELD_TYPE_MESSAGE | fieldNum,
+                                               (const char*)dim.mValue.str_value.c_str(),
+                                               dim.mValue.str_value.length());
+                        }
                     } else {
                         protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
                     }
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 6384757..3a5be43 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -394,6 +394,167 @@
     EXPECT_EQ(1.1f, item16.mValue.float_value);
 }
 
+TEST(LogEventTest, TestStatsLogEventWrapperNoChain) {
+    Parcel parcel;
+    // tag id
+    parcel.writeInt32(1);
+    // elapsed realtime
+    parcel.writeInt64(1111L);
+    // wallclock time
+    parcel.writeInt64(2222L);
+    // no chain
+    parcel.writeInt32(0);
+    // 2 data
+    parcel.writeInt32(2);
+    // int 6
+    parcel.writeInt32(1);
+    parcel.writeInt32(6);
+    // long 10
+    parcel.writeInt32(2);
+    parcel.writeInt64(10);
+    parcel.setDataPosition(0);
+
+    StatsLogEventWrapper statsLogEventWrapper;
+    EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+    EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+    EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+    EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+    EXPECT_EQ(0, statsLogEventWrapper.getWorkChains().size());
+    EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+    EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+    EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+    LogEvent event(statsLogEventWrapper, -1);
+    EXPECT_EQ(1, event.GetTagId());
+    EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+    EXPECT_EQ(2, event.size());
+    EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+    EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+}
+
+TEST(LogEventTest, TestStatsLogEventWrapperWithChain) {
+    Parcel parcel;
+    // tag id
+    parcel.writeInt32(1);
+    // elapsed realtime
+    parcel.writeInt64(1111L);
+    // wallclock time
+    parcel.writeInt64(2222L);
+    // 3 chains
+    parcel.writeInt32(3);
+    // chain1, 2 nodes (1, "tag1") (2, "tag2")
+    parcel.writeInt32(2);
+    parcel.writeInt32(1);
+    parcel.writeString16(String16("tag1"));
+    parcel.writeInt32(2);
+    parcel.writeString16(String16("tag2"));
+    // chain2, 1 node (3, "tag3")
+    parcel.writeInt32(1);
+    parcel.writeInt32(3);
+    parcel.writeString16(String16("tag3"));
+    // chain3, 2 nodes (4, "") (5, "")
+    parcel.writeInt32(2);
+    parcel.writeInt32(4);
+    parcel.writeString16(String16(""));
+    parcel.writeInt32(5);
+    parcel.writeString16(String16(""));
+    // 2 data
+    parcel.writeInt32(2);
+    // int 6
+    parcel.writeInt32(1);
+    parcel.writeInt32(6);
+    // long 10
+    parcel.writeInt32(2);
+    parcel.writeInt64(10);
+    parcel.setDataPosition(0);
+
+    StatsLogEventWrapper statsLogEventWrapper;
+    EXPECT_EQ(NO_ERROR, statsLogEventWrapper.readFromParcel(&parcel));
+    EXPECT_EQ(1, statsLogEventWrapper.getTagId());
+    EXPECT_EQ(1111L, statsLogEventWrapper.getElapsedRealTimeNs());
+    EXPECT_EQ(2222L, statsLogEventWrapper.getWallClockTimeNs());
+    EXPECT_EQ(3, statsLogEventWrapper.getWorkChains().size());
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids.size());
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[0].uids[0]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].uids[1]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[0].tags.size());
+    EXPECT_EQ("tag1", statsLogEventWrapper.getWorkChains()[0].tags[0]);
+    EXPECT_EQ("tag2", statsLogEventWrapper.getWorkChains()[0].tags[1]);
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].uids.size());
+    EXPECT_EQ(3, statsLogEventWrapper.getWorkChains()[1].uids[0]);
+    EXPECT_EQ(1, statsLogEventWrapper.getWorkChains()[1].tags.size());
+    EXPECT_EQ("tag3", statsLogEventWrapper.getWorkChains()[1].tags[0]);
+    EXPECT_EQ(2, statsLogEventWrapper.getElements().size());
+    EXPECT_EQ(6, statsLogEventWrapper.getElements()[0].int_value);
+    EXPECT_EQ(10L, statsLogEventWrapper.getElements()[1].long_value);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].uids.size());
+    EXPECT_EQ(4, statsLogEventWrapper.getWorkChains()[2].uids[0]);
+    EXPECT_EQ(5, statsLogEventWrapper.getWorkChains()[2].uids[1]);
+    EXPECT_EQ(2, statsLogEventWrapper.getWorkChains()[2].tags.size());
+    EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[0]);
+    EXPECT_EQ("", statsLogEventWrapper.getWorkChains()[2].tags[1]);
+
+    LogEvent event(statsLogEventWrapper, -1);
+    EXPECT_EQ(1, event.GetTagId());
+    EXPECT_EQ(1111L, event.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event.GetLogdTimestampNs());
+    EXPECT_EQ(2, event.size());
+    EXPECT_EQ(6, event.getValues()[0].mValue.int_value);
+    EXPECT_EQ(10, event.getValues()[1].mValue.long_value);
+
+    LogEvent event1(statsLogEventWrapper, 0);
+
+    EXPECT_EQ(1, event1.GetTagId());
+    EXPECT_EQ(1111L, event1.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event1.GetLogdTimestampNs());
+    EXPECT_EQ(6, event1.size());
+    EXPECT_EQ(1, event1.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event1.getValues()[0].mField.getField());
+    EXPECT_EQ("tag1", event1.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2010182, event1.getValues()[1].mField.getField());
+    EXPECT_EQ(2, event1.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x2010201, event1.getValues()[2].mField.getField());
+    EXPECT_EQ("tag2", event1.getValues()[3].mValue.str_value);
+    EXPECT_EQ(0x2018282, event1.getValues()[3].mField.getField());
+    EXPECT_EQ(6, event1.getValues()[4].mValue.int_value);
+    EXPECT_EQ(0x20000, event1.getValues()[4].mField.getField());
+    EXPECT_EQ(10, event1.getValues()[5].mValue.long_value);
+    EXPECT_EQ(0x30000, event1.getValues()[5].mField.getField());
+
+    LogEvent event2(statsLogEventWrapper, 1);
+
+    EXPECT_EQ(1, event2.GetTagId());
+    EXPECT_EQ(1111L, event2.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event2.GetLogdTimestampNs());
+    EXPECT_EQ(4, event2.size());
+    EXPECT_EQ(3, event2.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event2.getValues()[0].mField.getField());
+    EXPECT_EQ("tag3", event2.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2018182, event2.getValues()[1].mField.getField());
+    EXPECT_EQ(6, event2.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x20000, event2.getValues()[2].mField.getField());
+    EXPECT_EQ(10, event2.getValues()[3].mValue.long_value);
+    EXPECT_EQ(0x30000, event2.getValues()[3].mField.getField());
+
+    LogEvent event3(statsLogEventWrapper, 2);
+
+    EXPECT_EQ(1, event3.GetTagId());
+    EXPECT_EQ(1111L, event3.GetElapsedTimestampNs());
+    EXPECT_EQ(2222L, event3.GetLogdTimestampNs());
+    EXPECT_EQ(6, event3.size());
+    EXPECT_EQ(4, event3.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0x2010101, event3.getValues()[0].mField.getField());
+    EXPECT_EQ("", event3.getValues()[1].mValue.str_value);
+    EXPECT_EQ(0x2010182, event3.getValues()[1].mField.getField());
+    EXPECT_EQ(5, event3.getValues()[2].mValue.int_value);
+    EXPECT_EQ(0x2010201, event3.getValues()[2].mField.getField());
+    EXPECT_EQ("", event3.getValues()[3].mValue.str_value);
+    EXPECT_EQ(0x2018282, event3.getValues()[3].mField.getField());
+    EXPECT_EQ(6, event3.getValues()[4].mValue.int_value);
+    EXPECT_EQ(0x20000, event3.getValues()[4].mField.getField());
+    EXPECT_EQ(10, event3.getValues()[5].mValue.long_value);
+    EXPECT_EQ(0x30000, event3.getValues()[5].mField.getField());
+}
 
 TEST(LogEventTest, TestBinaryFieldAtom) {
     Atom launcherAtom;
@@ -444,7 +605,44 @@
     EXPECT_EQ(orig_str, result_str);
 }
 
+TEST(LogEventTest, TestBinaryFieldAtom_empty) {
+    Atom launcherAtom;
+    auto launcher_event = launcherAtom.mutable_launcher_event();
+    launcher_event->set_action(stats::launcher::LauncherAction::LONGPRESS);
+    launcher_event->set_src_state(stats::launcher::LauncherState::OVERVIEW);
+    launcher_event->set_dst_state(stats::launcher::LauncherState::ALLAPPS);
 
+    // empty string.
+    string extension_str;
+
+    LogEvent event1(Atom::kLauncherEventFieldNumber, 1000);
+
+    event1.write((int32_t)stats::launcher::LauncherAction::LONGPRESS);
+    event1.write((int32_t)stats::launcher::LauncherState::OVERVIEW);
+    event1.write((int64_t)stats::launcher::LauncherState::ALLAPPS);
+    event1.write(extension_str);
+    event1.init();
+
+    ProtoOutputStream proto;
+    event1.ToProto(proto);
+
+    std::vector<uint8_t> outData;
+    outData.resize(proto.size());
+    size_t pos = 0;
+    auto iter = proto.data();
+    while (iter.readBuffer() != NULL) {
+        size_t toRead = iter.currentToRead();
+        std::memcpy(&(outData[pos]), iter.readBuffer(), toRead);
+        pos += toRead;
+        iter.rp()->move(toRead);
+    }
+
+    std::string result_str(outData.begin(), outData.end());
+    std::string orig_str;
+    launcherAtom.SerializeToString(&orig_str);
+
+    EXPECT_EQ(orig_str, result_str);
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
index fc6e420..266ea35 100644
--- a/cmds/statsd/tests/external/puller_util_test.cpp
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -80,7 +80,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -120,7 +120,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -154,7 +154,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   // 20->32->31
   // 20->22->21
@@ -190,7 +190,7 @@
       .WillRepeatedly(Return(hostUid));
   EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
       .WillRepeatedly(ReturnArg<0>());
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   // 20->32->31
   // 20->22->21
@@ -231,7 +231,7 @@
 
   sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
   EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
-  mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
 
   vector<vector<int>> actual;
   extractIntoVector(inputData, actual);
@@ -256,7 +256,7 @@
   inputData.push_back(event);
 
   sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
-  mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+  mapAndMergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
 
   EXPECT_EQ(2, (int)inputData.size());
 }
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 8e9b91d..f3bf6e7 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -46,6 +46,10 @@
     private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
     private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
     private static final String COMMAND_REGISTER_SIM_PHONE_ACCOUNT = "register-sim-phone-account";
+    private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
+    private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
+            "add-or-remove-call-companion-app";
+    private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
     private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
     private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
     private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -64,6 +68,9 @@
                 "usage: telecom set-phone-account-enabled <COMPONENT> <ID> <USER_SN>\n" +
                 "usage: telecom set-phone-account-disabled <COMPONENT> <ID> <USER_SN>\n" +
                 "usage: telecom register-phone-account <COMPONENT> <ID> <USER_SN> <LABEL>\n" +
+                "usage: telecom set-test-call-screening-app <PACKAGE>\n" +
+                "usage: telecom set-test-auto-mode-app <PACKAGE>\n" +
+                "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n" +
                 "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN> <LABEL> <ADDRESS>\n" +
                 "usage: telecom unregister-phone-account <COMPONENT> <ID> <USER_SN>\n" +
                 "usage: telecom set-default-dialer <PACKAGE>\n" +
@@ -113,6 +120,15 @@
             case COMMAND_REGISTER_PHONE_ACCOUNT:
                 runRegisterPhoneAccount();
                 break;
+            case COMMAND_SET_TEST_CALL_SCREENING_APP:
+                runSetTestCallScreeningApp();
+                break;
+            case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
+                runAddOrRemoveCallCompanionApp();
+                break;
+            case COMMAND_SET_TEST_AUTO_MODE_APP:
+                runSetTestAutoModeApp();
+                break;
             case COMMAND_REGISTER_SIM_PHONE_ACCOUNT:
                 runRegisterSimPhoneAccount();
                 break;
@@ -173,6 +189,23 @@
         System.out.println("Success - " + handle + " registered.");
     }
 
+    private void runSetTestCallScreeningApp() throws RemoteException {
+        final String packageName = nextArg();
+        mTelecomService.setTestDefaultCallScreeningApp(packageName);
+    }
+
+    private void runAddOrRemoveCallCompanionApp() throws RemoteException {
+        final String packageName = nextArgRequired();
+        String isAdded = nextArgRequired();
+        boolean isAddedBool = "1".equals(isAdded);
+        mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
+    }
+
+    private void runSetTestAutoModeApp() throws RemoteException {
+        final String packageName = nextArg();
+        mTelecomService.setTestAutoModeApp(packageName);
+    }
+
     private void runUnregisterPhoneAccount() throws RemoteException {
         final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
         mTelecomService.unregisterPhoneAccount(handle);
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 41c2e6c..c2e441b 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -24296,7 +24296,8 @@
 HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$RangeOption;ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
 HSPLandroid/icu/util/CodePointMap;->getRange(ILandroid/icu/util/CodePointMap$ValueFilter;Landroid/icu/util/CodePointMap$Range;)Z
 HSPLandroid/icu/util/CodePointMap;->iterator()Ljava/util/Iterator;
-HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;HSPLandroid/icu/util/Currency$1;-><init>()V
+HSPLandroid/icu/util/CodePointMap;->stringIterator(Ljava/lang/CharSequence;I)Landroid/icu/util/CodePointMap$StringIterator;
+HSPLandroid/icu/util/Currency$1;-><init>()V
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
 HSPLandroid/icu/util/Currency$1;->createInstance(Ljava/lang/String;Ljava/lang/Void;)Landroid/icu/util/Currency;
 HSPLandroid/icu/util/Currency$CurrencyUsage;-><init>(Ljava/lang/String;I)V
@@ -32940,8 +32941,8 @@
 HSPLandroid/view/IWindowManager;->isWindowTraceEnabled()Z
 HSPLandroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
 HSPLandroid/view/IWindowManager;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z;I)V
+HSPLandroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 HSPLandroid/view/IWindowManager;->prepareAppTransition(IZ)V
 HSPLandroid/view/IWindowManager;->reenableKeyguard(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowManager;->refreshScreenCaptureDisabled(I)V
@@ -51954,6 +51955,22 @@
 HSPLlibcore/timezone/TimeZoneFinder;->parseTimeZoneMappings(Lorg/xmlpull/v1/XmlPullParser;)Ljava/util/List;
 HSPLlibcore/timezone/TimeZoneFinder;->processCountryZones(Lorg/xmlpull/v1/XmlPullParser;Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)Z
 HSPLlibcore/timezone/TimeZoneFinder;->processXml(Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
+HSPLlibcore/timezone/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->close()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->finalize()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/timezone/ZoneInfoDB$TzData;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readHeader()V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
+HSPLlibcore/timezone/ZoneInfoDB$TzData;->validateOffset(II)V
+HSPLlibcore/timezone/ZoneInfoDB;->getInstance()Llibcore/timezone/ZoneInfoDB$TzData;
 HSPLlibcore/util/BasicLruCache;-><init>(I)V
 HSPLlibcore/util/BasicLruCache;->create(Ljava/lang/Object;)Ljava/lang/Object;
 HSPLlibcore/util/BasicLruCache;->entryEvicted(Ljava/lang/Object;Ljava/lang/Object;)V
@@ -52007,22 +52024,6 @@
 HSPLlibcore/util/ZoneInfo;->hashCode()I
 HSPLlibcore/util/ZoneInfo;->inDaylightTime(Ljava/util/Date;)Z
 HSPLlibcore/util/ZoneInfo;->readTimeZone(Ljava/lang/String;Llibcore/io/BufferIterator;J)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/Object;)Ljava/lang/Object;
-HSPLlibcore/util/ZoneInfoDB$TzData$1;->create(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->close()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->finalize()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->getAvailableIDs()[Ljava/lang/String;
-HSPLlibcore/util/ZoneInfoDB$TzData;->getBufferIterator(Ljava/lang/String;)Llibcore/io/BufferIterator;
-HSPLlibcore/util/ZoneInfoDB$TzData;->hasTimeZone(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadData(Ljava/lang/String;)Z
-HSPLlibcore/util/ZoneInfoDB$TzData;->loadTzDataWithFallback([Ljava/lang/String;)Llibcore/util/ZoneInfoDB$TzData;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZone(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->makeTimeZoneUncached(Ljava/lang/String;)Llibcore/util/ZoneInfo;
-HSPLlibcore/util/ZoneInfoDB$TzData;->readHeader()V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readIndex(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->readZoneTab(Llibcore/io/BufferIterator;II)V
-HSPLlibcore/util/ZoneInfoDB$TzData;->validateOffset(II)V
-HSPLlibcore/util/ZoneInfoDB;->getInstance()Llibcore/util/ZoneInfoDB$TzData;
 HSPLorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
 HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->putString(Ljava/nio/ByteBuffer;Ljava/lang/String;)V
 HSPLorg/apache/harmony/dalvik/ddmc/ChunkHandler;->type(Ljava/lang/String;)I
@@ -63464,6 +63465,9 @@
 Llibcore/timezone/TimeZoneFinder$SelectiveCountryTimeZonesExtractor;
 Llibcore/timezone/TimeZoneFinder$TimeZonesProcessor;
 Llibcore/timezone/TimeZoneFinder;
+Llibcore/timezone/ZoneInfoDB$TzData$1;
+Llibcore/timezone/ZoneInfoDB$TzData;
+Llibcore/timezone/ZoneInfoDB;
 Llibcore/util/BasicLruCache;
 Llibcore/util/CharsetUtils;
 Llibcore/util/CollectionUtils;
@@ -63478,9 +63482,6 @@
 Llibcore/util/ZoneInfo$OffsetInterval;
 Llibcore/util/ZoneInfo$WallTime;
 Llibcore/util/ZoneInfo;
-Llibcore/util/ZoneInfoDB$TzData$1;
-Llibcore/util/ZoneInfoDB$TzData;
-Llibcore/util/ZoneInfoDB;
 Lorg/apache/harmony/dalvik/NativeTestTarget;
 Lorg/apache/harmony/dalvik/ddmc/Chunk;
 Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;
diff --git a/config/hiddenapi-max-sdk-p-blacklist.txt b/config/hiddenapi-greylist-max-p.txt
similarity index 100%
rename from config/hiddenapi-max-sdk-p-blacklist.txt
rename to config/hiddenapi-greylist-max-p.txt
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-greylist.txt
similarity index 90%
rename from config/hiddenapi-light-greylist.txt
rename to config/hiddenapi-greylist.txt
index f78506b..579ef93 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -51,6 +51,7 @@
 Landroid/app/backup/IFullBackupRestoreObserver$Stub;-><init>()V
 Landroid/app/backup/IRestoreObserver$Stub;-><init>()V
 Landroid/app/DownloadManager;->restartDownload([J)V
+Landroid/app/IActivityController$Stub;-><init>()V
 Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
 Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
@@ -62,25 +63,35 @@
 Landroid/app/IActivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IActivityManager;
 Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
 Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
+Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
+Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
 Landroid/app/IActivityManager;->checkPermission(Ljava/lang/String;II)I
+Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
 Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
 Landroid/app/IActivityManager;->finishHeavyWeightApp()V
 Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
 Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
 Landroid/app/IActivityManager;->getAllStackInfos()Ljava/util/List;
 Landroid/app/IActivityManager;->getConfiguration()Landroid/content/res/Configuration;
+Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
+Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
 Landroid/app/IActivityManager;->getIntentForIntentSender(Landroid/content/IIntentSender;)Landroid/content/Intent;
 Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
 Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
 Landroid/app/IActivityManager;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager;->getLockTaskModeState()I
 Landroid/app/IActivityManager;->getMemoryInfo(Landroid/app/ActivityManager$MemoryInfo;)V
 Landroid/app/IActivityManager;->getPackageProcessState(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/app/IActivityManager;->getProcessLimit()I
+Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
 Landroid/app/IActivityManager;->getProcessPss([I)[J
 Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
+Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
+Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
 Landroid/app/IActivityManager;->getServices(II)Ljava/util/List;
 Landroid/app/IActivityManager;->getTaskBounds(I)Landroid/graphics/Rect;
 Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
+Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/app/IActivityManager;->handleApplicationStrictModeViolation(Landroid/os/IBinder;ILandroid/os/StrictMode$ViolationInfo;)V
 Landroid/app/IActivityManager;->hang(Landroid/os/IBinder;Z)V
 Landroid/app/IActivityManager;->isInLockTaskMode()Z
@@ -98,9 +109,11 @@
 Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
 Landroid/app/IActivityManager;->registerProcessObserver(Landroid/app/IProcessObserver;)V
 Landroid/app/IActivityManager;->registerReceiver(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/IIntentReceiver;Landroid/content/IntentFilter;Ljava/lang/String;II)Landroid/content/Intent;
+Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
 Landroid/app/IActivityManager;->registerUserSwitchObserver(Landroid/app/IUserSwitchObserver;Ljava/lang/String;)V
 Landroid/app/IActivityManager;->removeContentProviderExternal(Ljava/lang/String;Landroid/os/IBinder;)V
 Landroid/app/IActivityManager;->removeStack(I)V
+Landroid/app/IActivityManager;->removeTask(I)Z
 Landroid/app/IActivityManager;->requestBugReport(I)V
 Landroid/app/IActivityManager;->resizeDockedStack(Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
 Landroid/app/IActivityManager;->resizeStack(ILandroid/graphics/Rect;ZZZI)V
@@ -120,8 +133,12 @@
 Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
 Landroid/app/IActivityManager;->setTaskResizeable(II)V
 Landroid/app/IActivityManager;->shutdown(I)Z
+Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
+Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
+Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
 Landroid/app/IActivityManager;->startBinderTracking()Z
 Landroid/app/IActivityManager;->startInstrumentation(Landroid/content/ComponentName;Ljava/lang/String;ILandroid/os/Bundle;Landroid/app/IInstrumentationWatcher;Landroid/app/IUiAutomationConnection;ILjava/lang/String;)Z
+Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
 Landroid/app/IActivityManager;->startSystemLockTaskMode(I)V
 Landroid/app/IActivityManager;->startUserInBackground(I)Z
 Landroid/app/IActivityManager;->stopAppSwitches()V
@@ -144,12 +161,16 @@
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
 Landroid/app/IAlarmManager;->getNextAlarmClock(I)Landroid/app/AlarmManager$AlarmClockInfo;
 Landroid/app/IAlarmManager;->set(Ljava/lang/String;IJJJILandroid/app/PendingIntent;Landroid/app/IAlarmListener;Ljava/lang/String;Landroid/os/WorkSource;Landroid/app/AlarmManager$AlarmClockInfo;)V
+Landroid/app/IAlarmManager;->setTime(J)Z
 Landroid/app/IApplicationThread;->scheduleBindService(Landroid/os/IBinder;Landroid/content/Intent;ZI)V
 Landroid/app/IApplicationThread;->scheduleCreateService(Landroid/os/IBinder;Landroid/content/pm/ServiceInfo;Landroid/content/res/CompatibilityInfo;I)V
 Landroid/app/IApplicationThread;->scheduleStopService(Landroid/os/IBinder;)V
 Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
 Landroid/app/IApplicationThread;->scheduleUnbindService(Landroid/os/IBinder;Landroid/content/Intent;)V
 Landroid/app/IAppTask;->getTaskInfo()Landroid/app/ActivityManager$RecentTaskInfo;
+Landroid/app/IAssistDataReceiver$Stub;-><init>()V
+Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
+Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IInputForwarder;->forwardEvent(Landroid/view/InputEvent;)Z
 Landroid/app/IInstrumentationWatcher$Stub;-><init>()V
 Landroid/app/IInstrumentationWatcher$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IInstrumentationWatcher;
@@ -236,6 +257,7 @@
 Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->getRemoteAlias(Landroid/bluetooth/BluetoothDevice;)Ljava/lang/String;
 Landroid/bluetooth/IBluetooth;->isEnabled()Z
+Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
 Landroid/bluetooth/IBluetoothA2dp$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothA2dp$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothA2dp;
 Landroid/bluetooth/IBluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
@@ -266,6 +288,10 @@
 Landroid/bluetooth/IBluetoothManagerCallback$Stub;-><init>()V
 Landroid/bluetooth/IBluetoothPbap$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetoothPbap;
 Landroid/bluetooth/IBluetoothStateChangeCallback$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
+Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
 Landroid/content/ContentProviderProxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/IClipboard$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/IClipboard$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/IClipboard;
@@ -312,11 +338,13 @@
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
+Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
 Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
 Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
@@ -325,6 +353,7 @@
 Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
 Landroid/content/pm/IPackageDeleteObserver2$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver2;
 Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V
+Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
 Landroid/content/pm/IPackageInstaller;->uninstall(Landroid/content/pm/VersionedPackage;Ljava/lang/String;ILandroid/content/IntentSender;I)V
 Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageInstallerCallback$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -364,11 +393,14 @@
 Landroid/content/pm/IPackageManager;->clearPackagePreferredActivities(Ljava/lang/String;)V
 Landroid/content/pm/IPackageManager;->currentToCanonicalPackageNames([Ljava/lang/String;)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->deleteApplicationCacheFiles(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)V
+Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
 Landroid/content/pm/IPackageManager;->getApplicationEnabledSetting(Ljava/lang/String;I)I
+Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
 Landroid/content/pm/IPackageManager;->getAppOpPermissionPackages(Ljava/lang/String;)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->getBlockUninstallForUser(Ljava/lang/String;I)Z
 Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
 Landroid/content/pm/IPackageManager;->getFlagsForUid(I)I
+Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
 Landroid/content/pm/IPackageManager;->getInstalledApplications(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
@@ -376,6 +408,7 @@
 Landroid/content/pm/IPackageManager;->getInstrumentationInfo(Landroid/content/ComponentName;I)Landroid/content/pm/InstrumentationInfo;
 Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
 Landroid/content/pm/IPackageManager;->getNameForUid(I)Ljava/lang/String;
+Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageManager;->getPackageInstaller()Landroid/content/pm/IPackageInstaller;
 Landroid/content/pm/IPackageManager;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->getPackageUid(Ljava/lang/String;II)I
@@ -415,6 +448,7 @@
 Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
 Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
 Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
+Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
 Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
 Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V
@@ -438,6 +472,7 @@
 Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I
 Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z
 Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V
+Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
 Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
 Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
@@ -447,7 +482,10 @@
 Landroid/location/ICountryListener$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;-><init>()V
 Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider;
+Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
+Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
 Landroid/location/IGeofenceProvider$Stub;-><init>()V
+Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
 Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/location/ILocationListener$Stub;-><init>()V
@@ -460,6 +498,10 @@
 Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager;
 Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I
 Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List;
+Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
+Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
+Landroid/location/INetInitiatedListener$Stub;-><init>()V
+Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
 Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V
 Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String;
 Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String;
@@ -486,6 +528,17 @@
 Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V
 Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z
 Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager;
+Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
+Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
+Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
+Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
 Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
 Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
@@ -499,6 +552,7 @@
 Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
 Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
 Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I
 Landroid/net/IConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String;
@@ -508,9 +562,12 @@
 Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager;->reportInetCondition(II)V
 Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
+Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
+Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
 Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
 Landroid/net/INetworkPolicyListener$Stub;-><init>()V
 Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
+Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
 Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z
 Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I
 Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V
@@ -520,14 +577,19 @@
 Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
 Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
 Landroid/net/INetworkStatsService;->forceUpdate()V
 Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats;
 Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String;
 Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession;
 Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession;
 Landroid/net/INetworkStatsSession;->close()V
+Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
+Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
 Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats;
 Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
+Landroid/net/InterfaceConfiguration;-><init>()V
+Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
 Landroid/net/MobileLinkQualityInfo;-><init>()V
 Landroid/net/nsd/INsdManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/nsd/INsdManager;
 Landroid/net/nsd/INsdManager;->getMessenger()Landroid/os/Messenger;
@@ -552,7 +614,12 @@
 Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
 Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
 Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
+Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
+Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
 Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
+Landroid/os/AsyncResult;->result:Ljava/lang/Object;
+Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
 Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
 Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
 Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -569,18 +636,24 @@
 Landroid/os/BatteryManager;->EXTRA_MAX_CHARGING_VOLTAGE:Ljava/lang/String;
 Landroid/os/BatteryStats$Counter;-><init>()V
 Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats$HistoryItem;-><init>()V
 Landroid/os/BatteryStats$HistoryItem;->batteryHealth:B
+Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
 Landroid/os/BatteryStats$HistoryItem;->batteryPlugType:B
 Landroid/os/BatteryStats$HistoryItem;->batteryStatus:B
 Landroid/os/BatteryStats$HistoryItem;->batteryVoltage:C
 Landroid/os/BatteryStats$HistoryItem;->clear()V
+Landroid/os/BatteryStats$HistoryItem;->cmd:B
 Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
 Landroid/os/BatteryStats$HistoryItem;->next:Landroid/os/BatteryStats$HistoryItem;
 Landroid/os/BatteryStats$HistoryItem;->same(Landroid/os/BatteryStats$HistoryItem;)Z
 Landroid/os/BatteryStats$HistoryItem;->setTo(JBLandroid/os/BatteryStats$HistoryItem;)V
 Landroid/os/BatteryStats$HistoryItem;->setTo(Landroid/os/BatteryStats$HistoryItem;)V
 Landroid/os/BatteryStats$HistoryItem;->states2:I
+Landroid/os/BatteryStats$HistoryItem;->states:I
+Landroid/os/BatteryStats$HistoryItem;->time:J
 Landroid/os/BatteryStats$Timer;-><init>()V
+Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
 Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
 Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStarts(I)I
@@ -604,6 +677,8 @@
 Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid$Sensor;->GPS:I
 Landroid/os/BatteryStats$Uid$Wakelock;-><init>()V
+Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
+Landroid/os/BatteryStats$Uid;-><init>()V
 Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
 Landroid/os/BatteryStats$Uid;->getMobileRadioActiveTime(I)J
@@ -616,6 +691,7 @@
 Landroid/os/BatteryStats$Uid;->getWakelockStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid;->getWifiBatchedScanTime(IJI)J
 Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
+Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
 Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
 Landroid/os/BatteryStats;-><init>()V
 Landroid/os/BatteryStats;->computeBatteryRealtime(JI)J
@@ -627,6 +703,7 @@
 Landroid/os/BatteryStats;->getGlobalWifiRunningTime(JI)J
 Landroid/os/BatteryStats;->getMobileRadioActiveTime(JI)J
 Landroid/os/BatteryStats;->getNetworkActivityBytes(II)J
+Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
 Landroid/os/BatteryStats;->getPhoneOnTime(JI)J
 Landroid/os/BatteryStats;->getPhoneSignalStrengthTime(IJI)J
 Landroid/os/BatteryStats;->getScreenBrightnessTime(IJI)J
@@ -640,6 +717,10 @@
 Landroid/os/BatteryStats;->WAKE_TYPE_PARTIAL:I
 Landroid/os/Binder;->execTransact(IJJI)Z
 Landroid/os/Binder;->mObject:J
+Landroid/os/Broadcaster;-><init>()V
+Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
+Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
+Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
 Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String;
 Landroid/os/Build;->getLong(Ljava/lang/String;)J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -713,6 +794,7 @@
 Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File;
+Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
 Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File;
 Landroid/os/Environment;->initForCurrentUser()V
 Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
@@ -733,12 +815,15 @@
 Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
 Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
+Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
 Landroid/os/Handler;-><init>(Z)V
 Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
+Landroid/os/Handler;->getMain()Landroid/os/Handler;
 Landroid/os/Handler;->getPostMessage(Ljava/lang/Runnable;Ljava/lang/Object;)Landroid/os/Message;
 Landroid/os/Handler;->mCallback:Landroid/os/Handler$Callback;
 Landroid/os/Handler;->mLooper:Landroid/os/Looper;
 Landroid/os/Handler;->mMessenger:Landroid/os/IMessenger;
+Landroid/os/HwBinder;->reportSyspropChanged()V
 Landroid/os/HwParcel;-><init>(Z)V
 Landroid/os/HwRemoteBinder;-><init>()V
 Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -749,16 +834,26 @@
 Landroid/os/IDeviceIdleController;->getAppIdTempWhitelist()[I
 Landroid/os/IDeviceIdleController;->getFullPowerWhitelistExceptIdle()[Ljava/lang/String;
 Landroid/os/INetworkManagementService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
+Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->disableNat(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->enableNat(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->getInterfaceConfig(Ljava/lang/String;)Landroid/net/InterfaceConfiguration;
 Landroid/os/INetworkManagementService;->getIpForwardingEnabled()Z
+Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
 Landroid/os/INetworkManagementService;->isTetheringStarted()Z
 Landroid/os/INetworkManagementService;->listTetheredInterfaces()[Ljava/lang/String;
+Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
+Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
+Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
 Landroid/os/INetworkManagementService;->setIpForwardingEnabled(Z)V
+Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
 Landroid/os/INetworkManagementService;->startTethering([Ljava/lang/String;)V
 Landroid/os/INetworkManagementService;->stopTethering()V
 Landroid/os/INetworkManagementService;->tetherInterface(Ljava/lang/String;)V
+Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
 Landroid/os/INetworkManagementService;->untetherInterface(Ljava/lang/String;)V
 Landroid/os/IPermissionController$Stub$Proxy;->checkPermission(Ljava/lang/String;II)Z
 Landroid/os/IPermissionController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPermissionController;
@@ -767,12 +862,15 @@
 Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IPowerManager;
 Landroid/os/IPowerManager$Stub;->TRANSACTION_acquireWakeLock:I
 Landroid/os/IPowerManager$Stub;->TRANSACTION_goToSleep:I
+Landroid/os/IPowerManager;->goToSleep(JII)V
 Landroid/os/IPowerManager;->isInteractive()Z
 Landroid/os/IPowerManager;->nap(J)V
+Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
 Landroid/os/IPowerManager;->releaseWakeLock(Landroid/os/IBinder;I)V
 Landroid/os/IPowerManager;->userActivity(JII)V
 Landroid/os/IPowerManager;->wakeUp(JLjava/lang/String;Ljava/lang/String;)V
 Landroid/os/IRecoverySystem$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IRecoverySystem;
+Landroid/os/IRemoteCallback$Stub;-><init>()V
 Landroid/os/IRemoteCallback;->sendResult(Landroid/os/Bundle;)V
 Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
@@ -798,6 +896,7 @@
 Landroid/os/Message;->markInUse()V
 Landroid/os/Message;->next:Landroid/os/Message;
 Landroid/os/Message;->recycleUnchecked()V
+Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
 Landroid/os/Message;->target:Landroid/os/Handler;
 Landroid/os/Message;->toString(J)Ljava/lang/String;
 Landroid/os/Message;->when:J
@@ -817,13 +916,16 @@
 Landroid/os/Parcel;->mNativePtr:J
 Landroid/os/Parcel;->readArrayMap(Landroid/util/ArrayMap;Ljava/lang/ClassLoader;)V
 Landroid/os/Parcel;->readArraySet(Ljava/lang/ClassLoader;)Landroid/util/ArraySet;
+Landroid/os/Parcel;->readBlob()[B
 Landroid/os/Parcel;->readCharSequence()Ljava/lang/CharSequence;
 Landroid/os/Parcel;->readCreator(Landroid/os/Parcelable$Creator;Ljava/lang/ClassLoader;)Landroid/os/Parcelable;
 Landroid/os/Parcel;->readExceptionCode()I
 Landroid/os/Parcel;->readParcelableCreator(Ljava/lang/ClassLoader;)Landroid/os/Parcelable$Creator;
 Landroid/os/Parcel;->readRawFileDescriptor()Ljava/io/FileDescriptor;
+Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
 Landroid/os/Parcel;->writeArrayMap(Landroid/util/ArrayMap;)V
 Landroid/os/Parcel;->writeArraySet(Landroid/util/ArraySet;)V
+Landroid/os/Parcel;->writeBlob([B)V
 Landroid/os/Parcel;->writeCharSequence(Ljava/lang/CharSequence;)V
 Landroid/os/Parcel;->writeParcelableCreator(Landroid/os/Parcelable;)V
 Landroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V
@@ -890,15 +992,27 @@
 Landroid/os/Process;->VPN_UID:I
 Landroid/os/Process;->WIFI_UID:I
 Landroid/os/RecoverySystem;->verifyPackageCompatibility(Ljava/io/InputStream;)Z
+Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/Registrant;->clear()V
 Landroid/os/Registrant;->getHandler()Landroid/os/Handler;
 Landroid/os/Registrant;->messageForRegistrant()Landroid/os/Message;
+Landroid/os/Registrant;->notifyRegistrant()V
+Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
 Landroid/os/Registrant;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;-><init>()V
 Landroid/os/RegistrantList;->add(Landroid/os/Handler;ILjava/lang/Object;)V
+Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
+Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
 Landroid/os/RegistrantList;->get(I)Ljava/lang/Object;
+Landroid/os/RegistrantList;->notifyRegistrants()V
+Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
 Landroid/os/RegistrantList;->notifyResult(Ljava/lang/Object;)V
+Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
+Landroid/os/RegistrantList;->removeCleared()V
 Landroid/os/RegistrantList;->size()I
 Landroid/os/RemoteCallback;->mHandler:Landroid/os/Handler;
 Landroid/os/RemoteCallbackList;->mCallbacks:Landroid/util/ArrayMap;
+Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
 Landroid/os/SELinux;->checkSELinuxAccess(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
 Landroid/os/SELinux;->getContext()Ljava/lang/String;
 Landroid/os/SELinux;->getFileContext(Ljava/lang/String;)Ljava/lang/String;
@@ -920,6 +1034,7 @@
 Landroid/os/ServiceManagerProxy;->getService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/ServiceManagerProxy;->mRemote:Landroid/os/IBinder;
 Landroid/os/ServiceSpecificException;-><init>(ILjava/lang/String;)V
+Landroid/os/ServiceSpecificException;->errorCode:I
 Landroid/os/SharedMemory;->getFd()I
 Landroid/os/ShellCommand;->peekNextArg()Ljava/lang/String;
 Landroid/os/StatFs;->mStat:Landroid/system/StructStatVfs;
@@ -927,6 +1042,7 @@
 Landroid/os/storage/IObbActionListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IObbActionListener;
 Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
+Landroid/os/storage/StorageEventListener;-><init>()V
 Landroid/os/StrictMode$Span;->finish()V
 Landroid/os/StrictMode$ThreadPolicy;->mask:I
 Landroid/os/StrictMode$VmPolicy$Builder;->mMask:I
@@ -954,8 +1070,12 @@
 Landroid/os/SystemProperties;->native_get_long(Ljava/lang/String;J)J
 Landroid/os/SystemProperties;->native_set(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/os/SystemProperties;->PROP_NAME_MAX:I
+Landroid/os/SystemProperties;->reportSyspropChanged()V
 Landroid/os/SystemProperties;->sChangeCallbacks:Ljava/util/ArrayList;
 Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->start(Ljava/lang/String;)V
+Landroid/os/SystemService;->stop(Ljava/lang/String;)V
+Landroid/os/SystemVibrator;-><init>()V
 Landroid/os/SystemVibrator;-><init>(Landroid/content/Context;)V
 Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
 Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
@@ -994,6 +1114,7 @@
 Landroid/os/UserHandle;->getUid(II)I
 Landroid/os/UserHandle;->getUserId(I)I
 Landroid/os/UserHandle;->isIsolated(I)Z
+Landroid/os/UserHandle;->isSameApp(II)Z
 Landroid/os/UserHandle;->mHandle:I
 Landroid/os/UserHandle;->MU_ENABLED:Z
 Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle;
@@ -1023,6 +1144,7 @@
 Landroid/os/UserManager;->getUserStartRealtime()J
 Landroid/os/UserManager;->getUserUnlockRealtime()J
 Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
+Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
 Landroid/os/UserManager;->isDeviceInDemoMode(Landroid/content/Context;)Z
 Landroid/os/UserManager;->isGuestUser(I)Z
 Landroid/os/UserManager;->isLinkedUser()Z
@@ -1072,6 +1194,7 @@
 Landroid/R$styleable;->CalendarView_weekDayTextAppearance:I
 Landroid/R$styleable;->CalendarView_weekNumberColor:I
 Landroid/R$styleable;->CalendarView_weekSeparatorLineColor:I
+Landroid/R$styleable;->CheckBoxPreference:[I
 Landroid/R$styleable;->CheckedTextView:[I
 Landroid/R$styleable;->CheckedTextView_checkMark:I
 Landroid/R$styleable;->CompoundButton:[I
@@ -1382,7 +1505,18 @@
 Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/telephony/CarrierMessagingServiceManager;-><init>()V
+Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
+Landroid/telephony/ims/compat/ImsService;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
 Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable;)V
+Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
+Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
+Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
+Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
 Landroid/telephony/SmsCbCmasInfo;->getCategory()I
 Landroid/telephony/SmsCbCmasInfo;->getCertainty()I
 Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I
@@ -1424,10 +1558,21 @@
 Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;-><init>()V
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
+Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
 Landroid/view/IDockedStackListener$Stub;-><init>()V
 Landroid/view/IGraphicsStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/IGraphicsStats$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IGraphicsStats;
+Landroid/view/IRecentsAnimationController;->finish(Z)V
+Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
 Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V
+Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
+Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
+Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
+Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
+Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
+Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
 Landroid/view/IRotationWatcher$Stub;-><init>()V
 Landroid/view/IWindow$Stub;-><init>()V
 Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow;
@@ -1435,10 +1580,13 @@
 Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
 Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
 Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
-Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
+Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z
 Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I
 Landroid/view/IWindowManager$Stub;-><init>()V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
+Landroid/view/IWindowManager;->endProlongedAnimations()V
 Landroid/view/IWindowManager;->executeAppTransition()V
 Landroid/view/IWindowManager;->freezeRotation(I)V
 Landroid/view/IWindowManager;->getAnimationScale(I)F
@@ -1447,21 +1595,24 @@
 Landroid/view/IWindowManager;->getDockedStackSide()I
 Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I
 Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V
-Landroid/view/IWindowManager;->hasNavigationBar()Z
+Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
+Landroid/view/IWindowManager;->hasNavigationBar(I)Z
 Landroid/view/IWindowManager;->isKeyguardLocked()Z
 Landroid/view/IWindowManager;->isKeyguardSecure()Z
 Landroid/view/IWindowManager;->isSafeModeEnabled()Z
 Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V
+Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V
 Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V
 Landroid/view/IWindowManager;->setAnimationScale(IF)V
 Landroid/view/IWindowManager;->setAnimationScales([F)V
 Landroid/view/IWindowManager;->setInTouchMode(Z)V
+Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
 Landroid/view/IWindowManager;->setShelfHeight(ZI)V
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager;->showStrictModeViolation(Z)V
 Landroid/view/IWindowManager;->thawRotation()V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession;
 Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V
 Landroid/view/IWindowSession;->getInTouchMode()Z
@@ -1497,6 +1648,7 @@
 Lcom/android/ims/ImsCall;->reject(I)V
 Lcom/android/ims/ImsCall;->terminate(I)V
 Lcom/android/ims/ImsConfigListener$Stub;-><init>()V
+Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
 Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V
 Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig;
 Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager;
@@ -1506,12 +1658,48 @@
 Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V
 Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V
 Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession;
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
+Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
 Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V
 Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
+Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
 Lcom/android/ims/internal/IImsService$Stub;-><init>()V
 Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService;
 Lcom/android/ims/internal/IImsUt$Stub;-><init>()V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
+Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V
 Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V
@@ -1522,7 +1710,52 @@
 Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
 Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
 Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
+Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
+Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
+Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
+Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
+Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
+Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
 Lcom/android/internal/app/AlertActivity;-><init>()V
 Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController;
 Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams;
@@ -1553,6 +1786,7 @@
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I
 Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I
+Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
 Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
 Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List;
 Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V
@@ -1600,6 +1834,7 @@
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I
 Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews;
 Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V
+Lcom/android/internal/content/PackageMonitor;-><init>()V
 Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V
 Lcom/android/internal/database/SortCursor;->mCursor:Landroid/database/Cursor;
 Lcom/android/internal/database/SortCursor;->mCursors:[Landroid/database/Cursor;
@@ -1613,6 +1848,14 @@
 Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lcom/android/internal/location/GpsNetInitiatedHandler$GpsNiNotification;)V
 Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
+Lcom/android/internal/location/ILocationProvider;->disable()V
+Lcom/android/internal/location/ILocationProvider;->enable()V
+Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
+Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
+Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
 Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
 Lcom/android/internal/net/VpnConfig;-><init>()V
@@ -1982,6 +2225,7 @@
 Lcom/android/internal/R$styleable;->MenuGroup:[I
 Lcom/android/internal/R$styleable;->MenuItem:[I
 Lcom/android/internal/R$styleable;->MenuView:[I
+Lcom/android/internal/R$styleable;->NumberPicker:[I
 Lcom/android/internal/R$styleable;->PopupWindow:[I
 Lcom/android/internal/R$styleable;->PopupWindow_popupAnimationStyle:I
 Lcom/android/internal/R$styleable;->PopupWindow_popupBackground:I
@@ -2107,6 +2351,7 @@
 Lcom/android/internal/R$styleable;->TextView_typeface:I
 Lcom/android/internal/R$styleable;->TextView_width:I
 Lcom/android/internal/R$styleable;->Theme:[I
+Lcom/android/internal/R$styleable;->TwoLineListItem:[I
 Lcom/android/internal/R$styleable;->View:[I
 Lcom/android/internal/R$styleable;->ViewAnimator:[I
 Lcom/android/internal/R$styleable;->ViewFlipper:[I
@@ -2615,7 +2860,6 @@
 Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
@@ -2697,7 +2941,6 @@
 Lcom/android/internal/telephony/dataconnection/DcTracker;->onSetUserDataEnabled(Z)V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
 Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerForAllDataDisconnected(Landroid/os/Handler;ILjava/lang/Object;)V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
@@ -2711,7 +2954,6 @@
 Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->updateRecords()V
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
 Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
@@ -3130,9 +3372,9 @@
 Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
 Lcom/android/internal/telephony/ITelephony;->getCallState()I
 Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
+Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
 Lcom/android/internal/telephony/ITelephony;->getDataState()I
 Lcom/android/internal/telephony/ITelephony;->getNetworkType()I
-Lcom/android/internal/telephony/ITelephony;->getVoiceMessageCount()I
 Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
@@ -3203,7 +3445,6 @@
 Lcom/android/internal/telephony/Phone;->isWifiCallingEnabled()Z
 Lcom/android/internal/telephony/Phone;->mCi:Lcom/android/internal/telephony/CommandsInterface;
 Lcom/android/internal/telephony/Phone;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/Phone;->mDcTracker:Lcom/android/internal/telephony/dataconnection/DcTracker;
 Lcom/android/internal/telephony/Phone;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
 Lcom/android/internal/telephony/Phone;->mImsPhone:Lcom/android/internal/telephony/Phone;
 Lcom/android/internal/telephony/Phone;->mMmiRegistrants:Landroid/os/RegistrantList;
@@ -3296,7 +3537,6 @@
 Lcom/android/internal/telephony/ProxyController;->mRadioCapabilitySessionId:I
 Lcom/android/internal/telephony/ProxyController;->mSetRadioAccessFamilyStatus:[I
 Lcom/android/internal/telephony/ProxyController;->mUniqueIdGenerator:Ljava/util/concurrent/atomic/AtomicInteger;
-Lcom/android/internal/telephony/ProxyController;->registerForAllDataDisconnected(ILandroid/os/Handler;ILjava/lang/Object;)V
 Lcom/android/internal/telephony/ProxyController;->sendRadioCapabilityRequest(IIIILjava/lang/String;II)V
 Lcom/android/internal/telephony/ProxyController;->sProxyController:Lcom/android/internal/telephony/ProxyController;
 Lcom/android/internal/telephony/RadioCapability;->getRadioAccessFamily()I
@@ -3396,7 +3636,6 @@
 Lcom/android/internal/telephony/ServiceStateTracker;->notifyDataRegStateRilRadioTechnologyChanged()V
 Lcom/android/internal/telephony/ServiceStateTracker;->notifySignalStrength()Z
 Lcom/android/internal/telephony/ServiceStateTracker;->pollState()V
-Lcom/android/internal/telephony/ServiceStateTracker;->powerOffRadioSafely(Lcom/android/internal/telephony/dataconnection/DcTracker;)V
 Lcom/android/internal/telephony/ServiceStateTracker;->reRegisterNetwork(Landroid/os/Message;)V
 Lcom/android/internal/telephony/ServiceStateTracker;->resetServiceStateInIwlanMode()V
 Lcom/android/internal/telephony/ServiceStateTracker;->setOperatorIdd(Ljava/lang/String;)V
@@ -4368,7 +4607,6 @@
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->SMILEY:Lcom/google/android/util/AbstractMessageParser$Token$Type;
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->YOUTUBE_VIDEO:Lcom/google/android/util/AbstractMessageParser$Token$Type;
-Lcom/sun/nio/file/ExtendedWatchEventModifier;->FILE_TREE:Lcom/sun/nio/file/ExtendedWatchEventModifier;
 Lgov/nist/core/Debug;->printStackTrace(Ljava/lang/Exception;)V
 Lgov/nist/core/GenericObject;-><init>()V
 Lgov/nist/core/GenericObject;->dbgPrint()V
@@ -4508,178 +4746,9 @@
 Lgov/nist/javax/sip/address/SipUri;->setUserParam(Ljava/lang/String;)V
 Lgov/nist/javax/sip/parser/URLParser;-><init>(Ljava/lang/String;)V
 Lgov/nist/javax/sip/parser/URLParser;->sipURL(Z)Lgov/nist/javax/sip/address/SipUri;
-Ljava/lang/DexCache;->dexFile:J
-Ljava/lang/invoke/SerializedLambda;-><init>(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
-Ljava/lang/invoke/SerializedLambda;->getCapturedArg(I)Ljava/lang/Object;
-Ljava/lang/invoke/SerializedLambda;->getCapturedArgCount()I
-Ljava/lang/invoke/SerializedLambda;->getCapturingClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getFunctionalInterfaceMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplClass()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodKind()I
-Ljava/lang/invoke/SerializedLambda;->getImplMethodName()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getImplMethodSignature()Ljava/lang/String;
-Ljava/lang/invoke/SerializedLambda;->getInstantiatedMethodType()Ljava/lang/String;
-Ljava/lang/UNIXProcess;->pid:I
-Ljava/net/AddressCache$AddressCacheEntry;-><init>(Ljava/lang/Object;)V
-Ljava/net/AddressCache$AddressCacheEntry;->expiryNanos:J
-Ljava/net/AddressCache$AddressCacheEntry;->value:Ljava/lang/Object;
-Ljava/net/AddressCache$AddressCacheKey;->mHostname:Ljava/lang/String;
-Ljava/net/AddressCache;->cache:Llibcore/util/BasicLruCache;
-Ljava/net/Inet6AddressImpl;->addressCache:Ljava/net/AddressCache;
-Ljava/net/PlainSocketImpl;-><init>()V
-Ljava/nio/DirectByteBuffer;->cleaner()Lsun/misc/Cleaner;
-Ljava/nio/file/FileTreeWalker;->followLinks:Z
-Ljava/nio/file/FileTreeWalker;->linkOptions:[Ljava/nio/file/LinkOption;
-Ljava/nio/file/FileTreeWalker;->maxDepth:I
-Ljava/util/zip/ZipFile$ZipEntryIterator;->nextElement()Ljava/util/zip/ZipEntry;
 Ljunit/framework/TestCase;->fName:Ljava/lang/String;
 Ljunit/framework/TestSuite;->isPublicTestMethod(Ljava/lang/reflect/Method;)Z
 Ljunit/framework/TestSuite;->isTestMethod(Ljava/lang/reflect/Method;)Z
-Llibcore/icu/DateIntervalFormat;->formatDateRange(JJILjava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->CACHED_PATTERNS:Llibcore/util/BasicLruCache;
-Llibcore/icu/ICU;->getBestDateTimePattern(Ljava/lang/String;Ljava/util/Locale;)Ljava/lang/String;
-Llibcore/icu/ICU;->getBestDateTimePatternNative(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
-Llibcore/icu/ICU;->getDateFormatOrder(Ljava/lang/String;)[C
-Llibcore/icu/LocaleData;->firstDayOfWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->get(Ljava/util/Locale;)Llibcore/icu/LocaleData;
-Llibcore/icu/LocaleData;->longStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->mapInvalidAndNullLocales(Ljava/util/Locale;)Ljava/util/Locale;
-Llibcore/icu/LocaleData;->minimalDaysInFirstWeek:Ljava/lang/Integer;
-Llibcore/icu/LocaleData;->shortMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneMonthNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->shortStandAloneWeekdayNames:[Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_Hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->timeFormat_hm:Ljava/lang/String;
-Llibcore/icu/LocaleData;->today:Ljava/lang/String;
-Llibcore/icu/LocaleData;->tomorrow:Ljava/lang/String;
-Llibcore/icu/LocaleData;->zeroDigit:C
-Llibcore/icu/TimeZoneNames;->forLocale(Ljava/util/Locale;)[Ljava/lang/String;
-Llibcore/io/AsynchronousCloseMonitor;->signalBlockedThreads(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/BlockGuardOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fchmod(Ljava/io/FileDescriptor;I)V
-Llibcore/io/BlockGuardOs;->fchown(Ljava/io/FileDescriptor;II)V
-Llibcore/io/BlockGuardOs;->fdatasync(Ljava/io/FileDescriptor;)V
-Llibcore/io/BlockGuardOs;->fstat(Ljava/io/FileDescriptor;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->fstatvfs(Ljava/io/FileDescriptor;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/BlockGuardOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->lseek(Ljava/io/FileDescriptor;JI)J
-Llibcore/io/BlockGuardOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/BlockGuardOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/BlockGuardOs;->posix_fallocate(Ljava/io/FileDescriptor;JJ)V
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pread(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;J)I
-Llibcore/io/BlockGuardOs;->pwrite(Ljava/io/FileDescriptor;[BIIJ)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->read(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->readv(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BlockGuardOs;->realpath(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/BlockGuardOs;->remove(Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/BlockGuardOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/BlockGuardOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
-Llibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;[BII)I
-Llibcore/io/BlockGuardOs;->writev(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I
-Llibcore/io/BufferIterator;->readByte()B
-Llibcore/io/BufferIterator;->readByteArray([BII)V
-Llibcore/io/BufferIterator;->readInt()I
-Llibcore/io/BufferIterator;->readIntArray([III)V
-Llibcore/io/BufferIterator;->seek(I)V
-Llibcore/io/BufferIterator;->skip(I)V
-Llibcore/io/DropBox;->addText(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;-><init>(Llibcore/io/Os;)V
-Llibcore/io/ForwardingOs;->access(Ljava/lang/String;I)Z
-Llibcore/io/ForwardingOs;->chmod(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->chown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->getenv(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->lchown(Ljava/lang/String;II)V
-Llibcore/io/ForwardingOs;->link(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->lstat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->mkdir(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->mkfifo(Ljava/lang/String;I)V
-Llibcore/io/ForwardingOs;->open(Ljava/lang/String;II)Ljava/io/FileDescriptor;
-Llibcore/io/ForwardingOs;->os:Llibcore/io/Os;
-Llibcore/io/ForwardingOs;->readlink(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/ForwardingOs;->remove(Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->removexattr(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/ForwardingOs;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/ForwardingOs;->setxattr(Ljava/lang/String;Ljava/lang/String;[BI)V
-Llibcore/io/ForwardingOs;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/ForwardingOs;->statvfs(Ljava/lang/String;)Landroid/system/StructStatVfs;
-Llibcore/io/ForwardingOs;->symlink(Ljava/lang/String;Ljava/lang/String;)V
-Llibcore/io/ForwardingOs;->sysconf(I)J
-Llibcore/io/ForwardingOs;->unlink(Ljava/lang/String;)V
-Llibcore/io/IoBridge;->isConnected(Ljava/io/FileDescriptor;Ljava/net/InetAddress;III)Z
-Llibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/lang/AutoCloseable;)V
-Llibcore/io/IoUtils;->closeQuietly(Ljava/net/Socket;)V
-Llibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
-Llibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
-Llibcore/io/MemoryMappedFile;->bigEndianIterator()Llibcore/io/BufferIterator;
-Llibcore/io/MemoryMappedFile;->mmapRO(Ljava/lang/String;)Llibcore/io/MemoryMappedFile;
-Llibcore/io/Os;->chmod(Ljava/lang/String;I)V
-Llibcore/io/Os;->close(Ljava/io/FileDescriptor;)V
-Llibcore/io/Os;->connect(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V
-Llibcore/io/Os;->gai_strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->remove(Ljava/lang/String;)V
-Llibcore/io/Os;->setenv(Ljava/lang/String;Ljava/lang/String;Z)V
-Llibcore/io/Os;->setsockoptTimeval(Ljava/io/FileDescriptor;IILandroid/system/StructTimeval;)V
-Llibcore/io/Os;->stat(Ljava/lang/String;)Landroid/system/StructStat;
-Llibcore/io/Os;->strerror(I)Ljava/lang/String;
-Llibcore/io/Os;->sysconf(I)J
-Llibcore/io/Streams;->readAsciiLine(Ljava/io/InputStream;)Ljava/lang/String;
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;)[B
-Llibcore/io/Streams;->readFully(Ljava/io/InputStream;[B)V
-Llibcore/io/Streams;->readSingleByte(Ljava/io/InputStream;)I
-Llibcore/io/Streams;->skipAll(Ljava/io/InputStream;)V
-Llibcore/io/Streams;->writeSingleByte(Ljava/io/OutputStream;I)V
-Llibcore/net/event/NetworkEventDispatcher;->addListener(Llibcore/net/event/NetworkEventListener;)V
-Llibcore/net/event/NetworkEventDispatcher;->getInstance()Llibcore/net/event/NetworkEventDispatcher;
-Llibcore/net/event/NetworkEventListener;-><init>()V
-Llibcore/net/http/HttpDate;->format(Ljava/util/Date;)Ljava/lang/String;
-Llibcore/net/http/HttpDate;->parse(Ljava/lang/String;)Ljava/util/Date;
-Llibcore/net/MimeUtils;->guessExtensionFromMimeType(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/MimeUtils;->guessMimeTypeFromExtension(Ljava/lang/String;)Ljava/lang/String;
-Llibcore/net/NetworkSecurityPolicy;->isCleartextTrafficPermitted()Z
-Llibcore/util/BasicLruCache;-><init>(I)V
-Llibcore/util/BasicLruCache;->evictAll()V
-Llibcore/util/BasicLruCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/BasicLruCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-Llibcore/util/EmptyArray;->BYTE:[B
-Llibcore/util/EmptyArray;->INT:[I
-Llibcore/util/EmptyArray;->OBJECT:[Ljava/lang/Object;
-Llibcore/util/ZoneInfoDB$TzData;-><init>()V
-Lorg/apache/harmony/dalvik/ddmc/Chunk;-><init>(ILjava/nio/ByteBuffer;)V
-Lorg/apache/harmony/dalvik/ddmc/ChunkHandler;->CHUNK_ORDER:Ljava/nio/ByteOrder;
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->broadcast(I)V
-Lorg/apache/harmony/dalvik/ddmc/DdmServer;->sendChunk(Lorg/apache/harmony/dalvik/ddmc/Chunk;)V
-Lorg/apache/harmony/dalvik/ddmc/DdmVmInternal;->getThreadStats()[B
-Lorg/apache/harmony/xml/dom/ElementImpl;->localName:Ljava/lang/String;
-Lorg/apache/harmony/xml/ExpatAttributes;-><init>()V
-Lorg/apache/harmony/xml/ExpatParser$EntityParser;->depth:I
-Lorg/apache/harmony/xml/ExpatParser;-><init>(Ljava/lang/String;Lorg/apache/harmony/xml/ExpatReader;ZLjava/lang/String;Ljava/lang/String;)V
-Lorg/apache/harmony/xml/ExpatParser;->append([BII)V
-Lorg/apache/harmony/xml/ExpatParser;->append([CII)V
-Lorg/apache/harmony/xml/ExpatParser;->attributes:Lorg/apache/harmony/xml/ExpatAttributes;
-Lorg/apache/harmony/xml/ExpatParser;->cloneAttributes()Lorg/xml/sax/Attributes;
-Lorg/apache/harmony/xml/ExpatParser;->finish()V
-Lorg/apache/harmony/xml/ExpatParser;->xmlReader:Lorg/apache/harmony/xml/ExpatReader;
-Lorg/apache/harmony/xml/ExpatReader;-><init>()V
-Lorg/apache/harmony/xml/ExpatReader;->contentHandler:Lorg/xml/sax/ContentHandler;
 Lorg/apache/xalan/extensions/ExpressionContext;->getContextNode()Lorg/w3c/dom/Node;
 Lorg/apache/xalan/extensions/ExpressionContext;->getErrorListener()Ljavax/xml/transform/ErrorListener;
 Lorg/apache/xalan/extensions/ExpressionContext;->getVariableOrParam(Lorg/apache/xml/utils/QName;)Lorg/apache/xpath/objects/XObject;
@@ -5358,431 +5427,3 @@
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutput(Ljava/io/Writer;)V
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setOutputProperty(Ljava/lang/String;Ljava/lang/String;)V
 Lorg/ccil/cowan/tagsoup/XMLWriter;->setPrefix(Ljava/lang/String;Ljava/lang/String;)V
-Lorg/xml/sax/helpers/NamespaceSupport$Context;-><init>(Lorg/xml/sax/helpers/NamespaceSupport;)V
-Lorg/xml/sax/helpers/ParserAdapter$AttributeListAdapter;-><init>(Lorg/xml/sax/helpers/ParserAdapter;)V
-Lsun/misc/ASCIICaseInsensitiveComparator;->CASE_INSENSITIVE_ORDER:Ljava/util/Comparator;
-Lsun/misc/ASCIICaseInsensitiveComparator;->lowerCaseHashCode(Ljava/lang/String;)I
-Lsun/misc/BASE64Decoder;-><init>()V
-Lsun/misc/BASE64Decoder;->pem_convert_array:[B
-Lsun/misc/BASE64Encoder;-><init>()V
-Lsun/misc/BASE64Encoder;->pem_array:[C
-Lsun/misc/CEFormatException;-><init>(Ljava/lang/String;)V
-Lsun/misc/CEStreamExhausted;-><init>()V
-Lsun/misc/CharacterDecoder;-><init>()V
-Lsun/misc/CharacterEncoder;-><init>()V
-Lsun/misc/CharacterEncoder;->encodeBuffer([B)Ljava/lang/String;
-Lsun/misc/CharacterEncoder;->encodeBufferPrefix(Ljava/io/OutputStream;)V
-Lsun/misc/CharacterEncoder;->pStream:Ljava/io/PrintStream;
-Lsun/misc/Cleaner;->create(Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
-Lsun/misc/FloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FloatingDecimal;->getHexDigit(Ljava/lang/String;I)I
-Lsun/misc/FloatingDecimal;->stripLeadingZeros(Ljava/lang/String;)Ljava/lang/String;
-Lsun/misc/FormattedFloatingDecimal$Form;->COMPATIBLE:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->DECIMAL_FLOAT:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal$Form;->SCIENTIFIC:Lsun/misc/FormattedFloatingDecimal$Form;
-Lsun/misc/FormattedFloatingDecimal;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->$assertionsDisabled:Z
-Lsun/misc/FpUtils;->rawCopySign(DD)D
-Lsun/misc/HexDumpEncoder;-><init>()V
-Lsun/misc/HexDumpEncoder;->currentByte:I
-Lsun/misc/HexDumpEncoder;->offset:I
-Lsun/misc/HexDumpEncoder;->thisLine:[B
-Lsun/misc/HexDumpEncoder;->thisLineLength:I
-Lsun/misc/IOUtils;->readFully(Ljava/io/InputStream;IZ)[B
-Lsun/misc/JarIndex;-><init>([Ljava/lang/String;)V
-Lsun/misc/JarIndex;->write(Ljava/io/OutputStream;)V
-Lsun/misc/MessageUtils;-><init>()V
-Lsun/misc/MetaIndex;->forJar(Ljava/io/File;)Lsun/misc/MetaIndex;
-Lsun/misc/MetaIndex;->registerDirectory(Ljava/io/File;)V
-Lsun/misc/VM;->maxDirectMemory()J
-Lsun/net/ftp/FtpClient;-><init>()V
-Lsun/net/util/IPAddressUtil;->isIPv4LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/util/IPAddressUtil;->isIPv6LiteralAddress(Ljava/lang/String;)Z
-Lsun/net/www/MessageHeader;-><init>()V
-Lsun/net/www/MessageHeader;-><init>(Ljava/io/InputStream;)V
-Lsun/net/www/MessageHeader;->add(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->findValue(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/MessageHeader;->prepend(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/MessageHeader;->print(Ljava/io/PrintStream;)V
-Lsun/net/www/MessageHeader;->set(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/net/www/ParseUtil;->decode(Ljava/lang/String;)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->encodePath(Ljava/lang/String;Z)Ljava/lang/String;
-Lsun/net/www/ParseUtil;->fileToEncodedURL(Ljava/io/File;)Ljava/net/URL;
-Lsun/net/www/URLConnection;-><init>(Ljava/net/URL;)V
-Lsun/net/www/URLConnection;->setProperties(Lsun/net/www/MessageHeader;)V
-Lsun/nio/ch/DirectBuffer;->address()J
-Lsun/nio/ch/FileChannelImpl;->unmap0(JJ)I
-Lsun/nio/ch/SelectorImpl;->publicSelectedKeys:Ljava/util/Set;
-Lsun/nio/ch/SelectorImpl;->selectedKeys:Ljava/util/Set;
-Lsun/nio/cs/HistoricallyNamedCharset;->historicalName()Ljava/lang/String;
-Lsun/nio/cs/ThreadLocalCoders;->decoderFor(Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
-Lsun/nio/fs/BasicFileAttributesHolder;->get()Ljava/nio/file/attribute/BasicFileAttributes;
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/Class;)V
-Lsun/reflect/misc/ReflectUtil;->checkPackageAccess(Ljava/lang/String;)V
-Lsun/reflect/misc/ReflectUtil;->isPackageAccessible(Ljava/lang/Class;)Z
-Lsun/reflect/misc/ReflectUtil;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/reflect/Reflection;->ensureMemberAccess(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
-Lsun/reflect/Reflection;->isSubclassOf(Ljava/lang/Class;Ljava/lang/Class;)Z
-Lsun/security/action/GetBooleanAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetIntegerAction;-><init>(Ljava/lang/String;I)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;)V
-Lsun/security/action/GetPropertyAction;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/jca/GetInstance$Instance;->impl:Ljava/lang/Object;
-Lsun/security/jca/GetInstance$Instance;->provider:Ljava/security/Provider;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/GetInstance;->getInstance(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;Ljava/security/Provider;)Lsun/security/jca/GetInstance$Instance;
-Lsun/security/jca/JCAUtil;->getSecureRandom()Ljava/security/SecureRandom;
-Lsun/security/jca/ProviderConfig;->argument:Ljava/lang/String;
-Lsun/security/jca/ProviderConfig;->CL_STRING:[Ljava/lang/Class;
-Lsun/security/jca/ProviderConfig;->disableLoad()V
-Lsun/security/jca/ProviderConfig;->hasArgument()Z
-Lsun/security/jca/ProviderList;->getService(Ljava/lang/String;Ljava/lang/String;)Ljava/security/Provider$Service;
-Lsun/security/jca/Providers;->getProviderList()Lsun/security/jca/ProviderList;
-Lsun/security/jca/Providers;->startJarVerification()Ljava/lang/Object;
-Lsun/security/jca/Providers;->stopJarVerification(Ljava/lang/Object;)V
-Lsun/security/pkcs/ContentInfo;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/ContentInfo;-><init>([B)V
-Lsun/security/pkcs/ContentInfo;->DATA_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/ContentInfo;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/pkcs/ContentInfo;->getData()[B
-Lsun/security/pkcs/ParsingException;-><init>(Ljava/lang/String;)V
-Lsun/security/pkcs/PKCS7;-><init>([B)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Ljava/security/cert/X509CRL;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;-><init>([Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/ContentInfo;[Ljava/security/cert/X509Certificate;[Lsun/security/pkcs/SignerInfo;)V
-Lsun/security/pkcs/PKCS7;->encodeSignedData(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS7;->getCertificates()[Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/PKCS7;->getContentInfo()Lsun/security/pkcs/ContentInfo;
-Lsun/security/pkcs/PKCS7;->getSignerInfos()[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify(Lsun/security/pkcs/SignerInfo;[B)Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS7;->verify([B)[Lsun/security/pkcs/SignerInfo;
-Lsun/security/pkcs/PKCS8Key;-><init>()V
-Lsun/security/pkcs/PKCS8Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/PKCS8Key;->encodedKey:[B
-Lsun/security/pkcs/PKCS8Key;->key:[B
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/pkcs/PKCS9Attribute;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/lang/Object;)V
-Lsun/security/pkcs/PKCS9Attribute;->CONTENT_TYPE_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attribute;->EMAIL_ADDRESS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->getValue()Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attribute;->MESSAGE_DIGEST_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attribute;->SIGNING_TIME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>(Lsun/security/util/DerInputStream;Z)V
-Lsun/security/pkcs/PKCS9Attributes;-><init>([Lsun/security/pkcs/PKCS9Attribute;)V
-Lsun/security/pkcs/PKCS9Attributes;->encode(BLjava/io/OutputStream;)V
-Lsun/security/pkcs/PKCS9Attributes;->getAttribute(Ljava/lang/String;)Lsun/security/pkcs/PKCS9Attribute;
-Lsun/security/pkcs/PKCS9Attributes;->getAttributeValue(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Object;
-Lsun/security/pkcs/PKCS9Attributes;->getDerEncoding()[B
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/pkcs/PKCS9Attributes;Lsun/security/x509/AlgorithmId;[BLsun/security/pkcs/PKCS9Attributes;)V
-Lsun/security/pkcs/SignerInfo;-><init>(Lsun/security/x509/X500Name;Ljava/math/BigInteger;Lsun/security/x509/AlgorithmId;Lsun/security/x509/AlgorithmId;[B)V
-Lsun/security/pkcs/SignerInfo;->getCertificate(Lsun/security/pkcs/PKCS7;)Ljava/security/cert/X509Certificate;
-Lsun/security/pkcs/SignerInfo;->getCertificateChain(Lsun/security/pkcs/PKCS7;)Ljava/util/ArrayList;
-Lsun/security/pkcs/SignerInfo;->getDigestAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getDigestEncryptionAlgorithmId()Lsun/security/x509/AlgorithmId;
-Lsun/security/pkcs/SignerInfo;->getEncryptedDigest()[B
-Lsun/security/provider/certpath/X509CertificatePair;->clearCache()V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
-Lsun/security/provider/certpath/X509CertPath;-><init>(Ljava/util/List;)V
-Lsun/security/provider/certpath/X509CertPath;->certs:Ljava/util/List;
-Lsun/security/provider/certpath/X509CertPath;->getEncodingsStatic()Ljava/util/Iterator;
-Lsun/security/provider/X509Factory;->addToCache(Lsun/security/util/Cache;[BLjava/lang/Object;)V
-Lsun/security/provider/X509Factory;->certCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->crlCache:Lsun/security/util/Cache;
-Lsun/security/provider/X509Factory;->getFromCache(Lsun/security/util/Cache;[B)Ljava/lang/Object;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509Certificate;)Lsun/security/x509/X509CertImpl;
-Lsun/security/provider/X509Factory;->intern(Ljava/security/cert/X509CRL;)Lsun/security/x509/X509CRLImpl;
-Lsun/security/timestamp/TimestampToken;-><init>([B)V
-Lsun/security/timestamp/TimestampToken;->getDate()Ljava/util/Date;
-Lsun/security/timestamp/TimestampToken;->getHashAlgorithm()Lsun/security/x509/AlgorithmId;
-Lsun/security/timestamp/TimestampToken;->getHashedMessage()[B
-Lsun/security/timestamp/TimestampToken;->getNonce()Ljava/math/BigInteger;
-Lsun/security/util/BitArray;-><init>(I[B)V
-Lsun/security/util/BitArray;->toByteArray()[B
-Lsun/security/util/Cache;-><init>()V
-Lsun/security/util/Cache;->clear()V
-Lsun/security/util/Cache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lsun/security/util/Cache;->newHardMemoryCache(I)Lsun/security/util/Cache;
-Lsun/security/util/Cache;->put(Ljava/lang/Object;Ljava/lang/Object;)V
-Lsun/security/util/Debug;->getInstance(Ljava/lang/String;)Lsun/security/util/Debug;
-Lsun/security/util/Debug;->println()V
-Lsun/security/util/Debug;->println(Ljava/lang/String;)V
-Lsun/security/util/Debug;->toHexString(Ljava/math/BigInteger;)Ljava/lang/String;
-Lsun/security/util/DerIndefLenConverter;-><init>()V
-Lsun/security/util/DerIndefLenConverter;->convert([B)[B
-Lsun/security/util/DerIndefLenConverter;->data:[B
-Lsun/security/util/DerIndefLenConverter;->dataPos:I
-Lsun/security/util/DerIndefLenConverter;->dataSize:I
-Lsun/security/util/DerIndefLenConverter;->isIndefinite(I)Z
-Lsun/security/util/DerIndefLenConverter;->newData:[B
-Lsun/security/util/DerIndefLenConverter;->numOfTotalLenBytes:I
-Lsun/security/util/DerIndefLenConverter;->parseLength()I
-Lsun/security/util/DerIndefLenConverter;->parseTag()V
-Lsun/security/util/DerIndefLenConverter;->parseValue(I)V
-Lsun/security/util/DerIndefLenConverter;->writeLengthAndValue()V
-Lsun/security/util/DerIndefLenConverter;->writeTag()V
-Lsun/security/util/DerInputStream;-><init>([B)V
-Lsun/security/util/DerInputStream;->available()I
-Lsun/security/util/DerInputStream;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerInputStream;->getBitString()[B
-Lsun/security/util/DerInputStream;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getInteger()I
-Lsun/security/util/DerInputStream;->getOctetString()[B
-Lsun/security/util/DerInputStream;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerInputStream;->getSequence(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(I)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getSet(IZ)[Lsun/security/util/DerValue;
-Lsun/security/util/DerInputStream;->getUTCTime()Ljava/util/Date;
-Lsun/security/util/DerInputStream;->getUTF8String()Ljava/lang/String;
-Lsun/security/util/DerInputStream;->mark(I)V
-Lsun/security/util/DerInputStream;->peekByte()I
-Lsun/security/util/DerInputStream;->reset()V
-Lsun/security/util/DerInputStream;->subStream(IZ)Lsun/security/util/DerInputStream;
-Lsun/security/util/DerInputStream;->tag:B
-Lsun/security/util/DerOutputStream;-><init>()V
-Lsun/security/util/DerOutputStream;-><init>(I)V
-Lsun/security/util/DerOutputStream;->putBitString([B)V
-Lsun/security/util/DerOutputStream;->putBoolean(Z)V
-Lsun/security/util/DerOutputStream;->putDerValue(Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putIA5String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putInteger(I)V
-Lsun/security/util/DerOutputStream;->putInteger(Ljava/math/BigInteger;)V
-Lsun/security/util/DerOutputStream;->putNull()V
-Lsun/security/util/DerOutputStream;->putOctetString([B)V
-Lsun/security/util/DerOutputStream;->putOID(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/util/DerOutputStream;->putOrderedSetOf(B[Lsun/security/util/DerEncoder;)V
-Lsun/security/util/DerOutputStream;->putPrintableString(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->putSequence([Lsun/security/util/DerValue;)V
-Lsun/security/util/DerOutputStream;->putUTCTime(Ljava/util/Date;)V
-Lsun/security/util/DerOutputStream;->putUTF8String(Ljava/lang/String;)V
-Lsun/security/util/DerOutputStream;->write(BLsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerOutputStream;->write(B[B)V
-Lsun/security/util/DerValue;-><init>(B[B)V
-Lsun/security/util/DerValue;-><init>(Ljava/io/InputStream;)V
-Lsun/security/util/DerValue;-><init>(Ljava/lang/String;)V
-Lsun/security/util/DerValue;-><init>([B)V
-Lsun/security/util/DerValue;-><init>([BII)V
-Lsun/security/util/DerValue;->buffer:Lsun/security/util/DerInputBuffer;
-Lsun/security/util/DerValue;->createTag(BZB)B
-Lsun/security/util/DerValue;->data:Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/util/DerValue;->getAsString()Ljava/lang/String;
-Lsun/security/util/DerValue;->getBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getBitString()[B
-Lsun/security/util/DerValue;->getData()Lsun/security/util/DerInputStream;
-Lsun/security/util/DerValue;->getDataBytes()[B
-Lsun/security/util/DerValue;->getOctetString()[B
-Lsun/security/util/DerValue;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/DerValue;->getPositiveBigInteger()Ljava/math/BigInteger;
-Lsun/security/util/DerValue;->getUnalignedBitString()Lsun/security/util/BitArray;
-Lsun/security/util/DerValue;->isConstructed()Z
-Lsun/security/util/DerValue;->isContextSpecific()Z
-Lsun/security/util/DerValue;->isContextSpecific(B)Z
-Lsun/security/util/DerValue;->isPrintableStringChar(C)Z
-Lsun/security/util/DerValue;->resetTag(B)V
-Lsun/security/util/DerValue;->tag:B
-Lsun/security/util/DerValue;->toByteArray()[B
-Lsun/security/util/DerValue;->toDerInputStream()Lsun/security/util/DerInputStream;
-Lsun/security/util/ManifestDigester$Entry;->digest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester$Entry;->digestWorkaround(Ljava/security/MessageDigest;)[B
-Lsun/security/util/ManifestDigester;-><init>([B)V
-Lsun/security/util/ManifestDigester;->get(Ljava/lang/String;Z)Lsun/security/util/ManifestDigester$Entry;
-Lsun/security/util/ManifestDigester;->manifestDigest(Ljava/security/MessageDigest;)[B
-Lsun/security/util/MemoryCache$HardCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;J)V
-Lsun/security/util/MemoryCache$SoftCacheEntry;-><init>(Ljava/lang/Object;Ljava/lang/Object;JLjava/lang/ref/ReferenceQueue;)V
-Lsun/security/util/ObjectIdentifier;-><init>(Ljava/lang/String;)V
-Lsun/security/util/ObjectIdentifier;-><init>([I)V
-Lsun/security/util/ObjectIdentifier;->equals(Lsun/security/util/ObjectIdentifier;)Z
-Lsun/security/util/ObjectIdentifier;->newInternal([I)Lsun/security/util/ObjectIdentifier;
-Lsun/security/util/PropertyExpander;->expand(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/ResourcesMgr;->getString(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/util/SecurityConstants;->CREATE_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->GET_CLASSLOADER_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREADGROUP_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SecurityConstants;->MODIFY_THREAD_PERMISSION:Ljava/lang/RuntimePermission;
-Lsun/security/util/SignatureFileVerifier;->isBlockOrSF(Ljava/lang/String;)Z
-Lsun/security/x509/AccessDescription;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/AccessDescription;->getAccessLocation()Lsun/security/x509/GeneralName;
-Lsun/security/x509/AccessDescription;->getAccessMethod()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;-><init>()V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;)V
-Lsun/security/x509/AlgorithmId;-><init>(Lsun/security/util/ObjectIdentifier;Ljava/security/AlgorithmParameters;)V
-Lsun/security/x509/AlgorithmId;->derEncode(Ljava/io/OutputStream;)V
-Lsun/security/x509/AlgorithmId;->DSA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->EC_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->encode()[B
-Lsun/security/x509/AlgorithmId;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/AlgorithmId;->equals(Lsun/security/x509/AlgorithmId;)Z
-Lsun/security/x509/AlgorithmId;->getAlgorithmId(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->getDigAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncAlgFromSigAlg(Ljava/lang/String;)Ljava/lang/String;
-Lsun/security/x509/AlgorithmId;->getEncodedParams()[B
-Lsun/security/x509/AlgorithmId;->getOID()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->getParameters()Ljava/security/AlgorithmParameters;
-Lsun/security/x509/AlgorithmId;->MD2_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->MD5_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->params:Lsun/security/util/DerValue;
-Lsun/security/x509/AlgorithmId;->parse(Lsun/security/util/DerValue;)Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/AlgorithmId;->RSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->sha1WithRSAEncryption_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA256_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA384_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA512_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AlgorithmId;->SHA_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AttributeNameEnumeration;-><init>()V
-Lsun/security/x509/AVA;-><init>(Lsun/security/util/ObjectIdentifier;Lsun/security/util/DerValue;)V
-Lsun/security/x509/AVA;->getDerValue()Lsun/security/util/DerValue;
-Lsun/security/x509/AVA;->getObjectIdentifier()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVA;->getValueString()Ljava/lang/String;
-Lsun/security/x509/AVA;->toRFC2253CanonicalString()Ljava/lang/String;
-Lsun/security/x509/AVAComparator;->INSTANCE:Ljava/util/Comparator;
-Lsun/security/x509/AVAKeyword;->getOID(Ljava/lang/String;ILjava/util/Map;)Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->isCompliant(I)Z
-Lsun/security/x509/AVAKeyword;->keyword:Ljava/lang/String;
-Lsun/security/x509/AVAKeyword;->keywordMap:Ljava/util/Map;
-Lsun/security/x509/AVAKeyword;->oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/AVAKeyword;->oidMap:Ljava/util/Map;
-Lsun/security/x509/CertificateAlgorithmId;-><init>(Lsun/security/x509/AlgorithmId;)V
-Lsun/security/x509/CertificateExtensions;-><init>()V
-Lsun/security/x509/CertificateExtensions;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/CertificateExtensions;->encode(Ljava/io/OutputStream;Z)V
-Lsun/security/x509/CertificateExtensions;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateExtensions;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/CertificateIssuerName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(I)V
-Lsun/security/x509/CertificateSerialNumber;-><init>(Ljava/math/BigInteger;)V
-Lsun/security/x509/CertificateSubjectName;-><init>(Lsun/security/x509/X500Name;)V
-Lsun/security/x509/CertificateSubjectName;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/CertificateValidity;-><init>(Ljava/util/Date;Ljava/util/Date;)V
-Lsun/security/x509/CertificateVersion;-><init>(I)V
-Lsun/security/x509/CertificateX509Key;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/CRLDistributionPointsExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;-><init>(Ljava/lang/Boolean;Ljava/lang/Object;)V
-Lsun/security/x509/CRLNumberExtension;->encodeThis()V
-Lsun/security/x509/CRLNumberExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/Extension;-><init>(Lsun/security/x509/Extension;)V
-Lsun/security/x509/Extension;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/Extension;->getExtensionId()Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/GeneralName;-><init>(Lsun/security/x509/GeneralNameInterface;)V
-Lsun/security/x509/GeneralName;->getName()Lsun/security/x509/GeneralNameInterface;
-Lsun/security/x509/GeneralName;->getType()I
-Lsun/security/x509/GeneralNames;-><init>()V
-Lsun/security/x509/GeneralNames;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/GeneralNames;->add(Lsun/security/x509/GeneralName;)Lsun/security/x509/GeneralNames;
-Lsun/security/x509/GeneralNames;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/GeneralNames;->isEmpty()Z
-Lsun/security/x509/KeyIdentifier;-><init>(Ljava/security/PublicKey;)V
-Lsun/security/x509/KeyIdentifier;->getIdentifier()[B
-Lsun/security/x509/KeyIdentifier;->octetString:[B
-Lsun/security/x509/KeyUsageExtension;-><init>([Z)V
-Lsun/security/x509/KeyUsageExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/NetscapeCertTypeExtension;-><init>([B)V
-Lsun/security/x509/NetscapeCertTypeExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/OIDMap$OIDInfo;->clazz:Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->getClass(Lsun/security/util/ObjectIdentifier;)Ljava/lang/Class;
-Lsun/security/x509/OIDMap;->nameMap:Ljava/util/Map;
-Lsun/security/x509/OIDMap;->oidMap:Ljava/util/Map;
-Lsun/security/x509/PKIXExtensions;->CertificateIssuer_Id:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/SerialNumber;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/SubjectAlternativeNameExtension;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/SubjectKeyIdentifierExtension;-><init>([B)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/UniqueIdentity;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/UniqueIdentity;->encode(Lsun/security/util/DerOutputStream;B)V
-Lsun/security/x509/URIName;->getName()Ljava/lang/String;
-Lsun/security/x509/URIName;->getScheme()Ljava/lang/String;
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerInputStream;)V
-Lsun/security/x509/X500Name;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X500Name;-><init>([B)V
-Lsun/security/x509/X500Name;->allAvas()Ljava/util/List;
-Lsun/security/x509/X500Name;->asX500Name(Ljavax/security/auth/x500/X500Principal;)Lsun/security/x509/X500Name;
-Lsun/security/x509/X500Name;->asX500Principal()Ljavax/security/auth/x500/X500Principal;
-Lsun/security/x509/X500Name;->commonName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->countryName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DNQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->DOMAIN_COMPONENT_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->encode(Lsun/security/util/DerOutputStream;)V
-Lsun/security/x509/X500Name;->GENERATIONQUALIFIER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->getCommonName()Ljava/lang/String;
-Lsun/security/x509/X500Name;->GIVENNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->INITIALS_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->ipAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->isEmpty()Z
-Lsun/security/x509/X500Name;->localityName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->orgUnitName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SERIALNUMBER_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->stateName_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->streetAddress_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->SURNAME_OID:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->title_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X500Name;->userid_oid:Lsun/security/util/ObjectIdentifier;
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;-><init>(Lsun/security/x509/X509CertInfo;)V
-Lsun/security/x509/X509CertImpl;-><init>([B)V
-Lsun/security/x509/X509CertImpl;->algId:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509CertImpl;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509CertImpl;->parse(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CertImpl;->readOnly:Z
-Lsun/security/x509/X509CertImpl;->sign(Ljava/security/PrivateKey;Ljava/lang/String;)V
-Lsun/security/x509/X509CertImpl;->signature:[B
-Lsun/security/x509/X509CertImpl;->signedCert:[B
-Lsun/security/x509/X509CertInfo;-><init>()V
-Lsun/security/x509/X509CertInfo;-><init>([B)V
-Lsun/security/x509/X509CertInfo;->get(Ljava/lang/String;)Ljava/lang/Object;
-Lsun/security/x509/X509CertInfo;->set(Ljava/lang/String;Ljava/lang/Object;)V
-Lsun/security/x509/X509CRLEntryImpl;->getExtension(Lsun/security/util/ObjectIdentifier;)Lsun/security/x509/Extension;
-Lsun/security/x509/X509CRLImpl;-><init>(Ljava/io/InputStream;)V
-Lsun/security/x509/X509CRLImpl;-><init>(Lsun/security/util/DerValue;)V
-Lsun/security/x509/X509CRLImpl;-><init>([B)V
-Lsun/security/x509/X509CRLImpl;->getEncodedInternal()[B
-Lsun/security/x509/X509Key;-><init>()V
-Lsun/security/x509/X509Key;->algid:Lsun/security/x509/AlgorithmId;
-Lsun/security/x509/X509Key;->encodedKey:[B
-Lsun/security/x509/X509Key;->key:[B
-Lsun/security/x509/X509Key;->parse(Lsun/security/util/DerValue;)Ljava/security/PublicKey;
-Lsun/security/x509/X509Key;->unusedBits:I
-Lsun/util/calendar/AbstractCalendar;->getDayOfWeekDateOnOrBefore(JI)J
-Lsun/util/calendar/AbstractCalendar;->getTimeOfDayValue(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/BaseCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/BaseCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/CalendarDate;->getDayOfMonth()I
-Lsun/util/calendar/CalendarDate;->getMonth()I
-Lsun/util/calendar/CalendarDate;->getTimeOfDay()J
-Lsun/util/calendar/CalendarDate;->getYear()I
-Lsun/util/calendar/CalendarDate;->setDate(III)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setDayOfMonth(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setHours(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMillis(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setMinutes(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarDate;->setSeconds(I)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->forName(Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
-Lsun/util/calendar/CalendarSystem;->getGregorianCalendar()Lsun/util/calendar/Gregorian;
-Lsun/util/calendar/CalendarSystem;->getTime(Lsun/util/calendar/CalendarDate;)J
-Lsun/util/calendar/CalendarSystem;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/CalendarSystem;->validate(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/CalendarUtils;->floorDivide(II)I
-Lsun/util/calendar/CalendarUtils;->floorDivide(JJ)J
-Lsun/util/calendar/CalendarUtils;->mod(II)I
-Lsun/util/calendar/CalendarUtils;->mod(JJ)J
-Lsun/util/calendar/Era;-><init>(Ljava/lang/String;Ljava/lang/String;JZ)V
-Lsun/util/calendar/Era;->getAbbreviation()Ljava/lang/String;
-Lsun/util/calendar/Era;->getName()Ljava/lang/String;
-Lsun/util/calendar/Era;->getSinceDate()Lsun/util/calendar/CalendarDate;
-Lsun/util/calendar/ImmutableGregorianDate;->unsupported()V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->getNormalizedYear()I
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setEra(Lsun/util/calendar/Era;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setNormalizedYear(I)V
-Lsun/util/calendar/LocalGregorianCalendar$Date;->setYear(I)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->newCalendarDate(Ljava/util/TimeZone;)Lsun/util/calendar/LocalGregorianCalendar$Date;
-Lsun/util/calendar/LocalGregorianCalendar;->normalize(Lsun/util/calendar/CalendarDate;)Z
-Lsun/util/calendar/LocalGregorianCalendar;->validate(Lsun/util/calendar/CalendarDate;)Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
deleted file mode 100644
index 575ba34..0000000
--- a/config/hiddenapi-vendor-list.txt
+++ /dev/null
@@ -1,247 +0,0 @@
-Landroid/app/IActivityController$Stub;-><init>()V
-Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V
-Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V
-Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V
-Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo;
-Landroid/app/IActivityManager;->getFilteredTasks(III)Ljava/util/List;
-Landroid/app/IActivityManager;->getLockTaskModeState()I
-Landroid/app/IActivityManager;->getProcessMemoryInfo([I)[Landroid/os/Debug$MemoryInfo;
-Landroid/app/IActivityManager;->getRecentTasks(III)Landroid/content/pm/ParceledListSlice;
-Landroid/app/IActivityManager;->getRunningAppProcesses()Ljava/util/List;
-Landroid/app/IActivityManager;->getTaskSnapshot(IZ)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/app/IActivityManager;->registerTaskStackListener(Landroid/app/ITaskStackListener;)V
-Landroid/app/IActivityManager;->removeTask(I)Z
-Landroid/app/IActivityManager;->startActivity(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startActivityAsUser(Landroid/app/IApplicationThread;Ljava/lang/String;Landroid/content/Intent;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;IILandroid/app/ProfilerInfo;Landroid/os/Bundle;I)I
-Landroid/app/IActivityManager;->startActivityFromRecents(ILandroid/os/Bundle;)I
-Landroid/app/IActivityManager;->startRecentsActivity(Landroid/content/Intent;Landroid/app/IAssistDataReceiver;Landroid/view/IRecentsAnimationRunner;)V
-Landroid/app/IAlarmManager;->setTime(J)Z
-Landroid/app/IAssistDataReceiver$Stub;-><init>()V
-Landroid/app/IAssistDataReceiver;->onHandleAssistData(Landroid/os/Bundle;)V
-Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
-Landroid/bluetooth/IBluetooth;->sendConnectionStateChange(Landroid/bluetooth/BluetoothDevice;III)V
-Landroid/companion/ICompanionDeviceDiscoveryService$Stub;-><init>()V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelected(Ljava/lang/String;ILjava/lang/String;)V
-Landroid/companion/ICompanionDeviceDiscoveryServiceCallback;->onDeviceSelectionCancel()V
-Landroid/companion/IFindDeviceCallback;->onSuccess(Landroid/app/PendingIntent;)V
-Landroid/content/pm/IPackageDataObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver$Stub;-><init>()V
-Landroid/content/pm/IPackageDeleteObserver;->packageDeleted(Ljava/lang/String;I)V
-Landroid/content/pm/IPackageManager;->getActivityInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
-Landroid/content/pm/IPackageManager;->getApplicationInfo(Ljava/lang/String;II)Landroid/content/pm/ApplicationInfo;
-Landroid/content/pm/IPackageManager;->getHomeActivities(Ljava/util/List;)Landroid/content/ComponentName;
-Landroid/content/pm/IPackageManager;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
-Landroid/content/pm/IPackageStatsObserver;->onGetStatsCompleted(Landroid/content/pm/PackageStats;Z)V
-Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V
-Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String;
-Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V
-Landroid/location/ILocationManager;->getNetworkProviderPackage()Ljava/lang/String;
-Landroid/location/ILocationManager;->reportLocation(Landroid/location/Location;Z)V
-Landroid/location/INetInitiatedListener$Stub;-><init>()V
-Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z
-Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V
-Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V
-Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V
-Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V
-Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState;
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
-Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo;
-Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
-Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
-Landroid/net/InterfaceConfiguration;-><init>()V
-Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange;
-Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable;
-Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)Landroid/os/AsyncResult;
-Landroid/os/AsyncResult;->result:Ljava/lang/Object;
-Landroid/os/AsyncResult;->userObj:Ljava/lang/Object;
-Landroid/os/BatteryStats$HistoryItem;-><init>()V
-Landroid/os/BatteryStats$HistoryItem;->batteryLevel:B
-Landroid/os/BatteryStats$HistoryItem;->cmd:B
-Landroid/os/BatteryStats$HistoryItem;->states:I
-Landroid/os/BatteryStats$HistoryItem;->time:J
-Landroid/os/BatteryStats$Timer;->getCountLocked(I)I
-Landroid/os/BatteryStats$Uid$Wakelock;->getWakeTime(I)Landroid/os/BatteryStats$Timer;
-Landroid/os/BatteryStats$Uid;-><init>()V
-Landroid/os/BatteryStats$Uid;->getWifiRunningTime(JI)J
-Landroid/os/BatteryStats;->getNextHistoryLocked(Landroid/os/BatteryStats$HistoryItem;)Z
-Landroid/os/Broadcaster;-><init>()V
-Landroid/os/Broadcaster;->broadcast(Landroid/os/Message;)V
-Landroid/os/Broadcaster;->cancelRequest(ILandroid/os/Handler;I)V
-Landroid/os/Broadcaster;->request(ILandroid/os/Handler;I)V
-Landroid/os/Environment;->getLegacyExternalStorageDirectory()Ljava/io/File;
-Landroid/os/Handler;-><init>(Landroid/os/Looper;Landroid/os/Handler$Callback;Z)V
-Landroid/os/Handler;->getMain()Landroid/os/Handler;
-Landroid/os/HwBinder;->reportSyspropChanged()V
-Landroid/os/INetworkManagementService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/INetworkManagementService;
-Landroid/os/INetworkManagementService;->clearInterfaceAddresses(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->disableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->enableIpv6(Ljava/lang/String;)V
-Landroid/os/INetworkManagementService;->isBandwidthControlEnabled()Z
-Landroid/os/INetworkManagementService;->registerObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/INetworkManagementService;->setInterfaceConfig(Ljava/lang/String;Landroid/net/InterfaceConfiguration;)V
-Landroid/os/INetworkManagementService;->setInterfaceIpv6PrivacyExtensions(Ljava/lang/String;Z)V
-Landroid/os/INetworkManagementService;->setIPv6AddrGenMode(Ljava/lang/String;I)V
-Landroid/os/INetworkManagementService;->unregisterObserver(Landroid/net/INetworkManagementEventObserver;)V
-Landroid/os/IPowerManager;->goToSleep(JII)V
-Landroid/os/IPowerManager;->reboot(ZLjava/lang/String;Z)V
-Landroid/os/IRemoteCallback$Stub;-><init>()V
-Landroid/os/Message;->setCallback(Ljava/lang/Runnable;)Landroid/os/Message;
-Landroid/os/Parcel;->readBlob()[B
-Landroid/os/Parcel;->readStringArray()[Ljava/lang/String;
-Landroid/os/Parcel;->writeBlob([B)V
-Landroid/os/Registrant;-><init>(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/Registrant;->clear()V
-Landroid/os/Registrant;->notifyRegistrant()V
-Landroid/os/Registrant;->notifyRegistrant(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;-><init>()V
-Landroid/os/RegistrantList;->add(Landroid/os/Registrant;)V
-Landroid/os/RegistrantList;->addUnique(Landroid/os/Handler;ILjava/lang/Object;)V
-Landroid/os/RegistrantList;->notifyRegistrants()V
-Landroid/os/RegistrantList;->notifyRegistrants(Landroid/os/AsyncResult;)V
-Landroid/os/RegistrantList;->remove(Landroid/os/Handler;)V
-Landroid/os/RegistrantList;->removeCleared()V
-Landroid/os/RemoteException;->rethrowFromSystemServer()Ljava/lang/RuntimeException;
-Landroid/os/ServiceSpecificException;->errorCode:I
-Landroid/os/storage/StorageEventListener;-><init>()V
-Landroid/os/SystemProperties;->reportSyspropChanged()V
-Landroid/os/SystemService;->start(Ljava/lang/String;)V
-Landroid/os/SystemService;->stop(Ljava/lang/String;)V
-Landroid/os/SystemVibrator;-><init>()V
-Landroid/os/UserHandle;->isSameApp(II)Z
-Landroid/os/UserManager;->hasUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
-Landroid/R$styleable;->CheckBoxPreference:[I
-Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
-Landroid/telephony/ims/compat/ImsService;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
-Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
-Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
-Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
-Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I
-Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I
-Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V
-Landroid/view/IRecentsAnimationController;->finish(Z)V
-Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot;
-Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V
-Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V
-Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V
-Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V
-Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
-Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
-Landroid/view/IWindowManager;->endProlongedAnimations()V
-Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
-Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
-Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V
-Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
-Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V
-Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
-Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V
-Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V
-Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V
-Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService;
-Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z
-Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z
-Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V
-Lcom/android/internal/content/PackageMonitor;-><init>()V
-Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
-Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
-Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
-Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
-Lcom/android/internal/R$styleable;->NumberPicker:[I
-Lcom/android/internal/R$styleable;->TwoLineListItem:[I
-Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 550e795..3095925 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -4117,6 +4117,10 @@
 com.android.internal.util.VirtualRefBasePtr
 com.android.internal.util.XmlUtils
 com.android.internal.util.XmlUtils$WriteMapCallback
+com.android.internal.util.function.NonaConsumer
+com.android.internal.util.function.NonaFunction
+com.android.internal.util.function.OctConsumer
+com.android.internal.util.function.OctFunction
 com.android.internal.util.function.HeptConsumer
 com.android.internal.util.function.HeptFunction
 com.android.internal.util.function.HexConsumer
@@ -6172,6 +6176,9 @@
 libcore.reflect.Types
 libcore.reflect.WildcardTypeImpl
 libcore.timezone.TimeZoneDataFiles
+libcore.timezone.ZoneInfoDB
+libcore.timezone.ZoneInfoDB$TzData
+libcore.timezone.ZoneInfoDB$TzData$1
 libcore.util.BasicLruCache
 libcore.util.CharsetUtils
 libcore.util.CollectionUtils
@@ -6184,9 +6191,6 @@
 libcore.util.ZoneInfo
 libcore.util.ZoneInfo$CheckedArithmeticException
 libcore.util.ZoneInfo$WallTime
-libcore.util.ZoneInfoDB
-libcore.util.ZoneInfoDB$TzData
-libcore.util.ZoneInfoDB$TzData$1
 org.apache.harmony.dalvik.NativeTestTarget
 org.apache.harmony.dalvik.ddmc.Chunk
 org.apache.harmony.dalvik.ddmc.ChunkHandler
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 83fab7e..f3f065a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -122,7 +122,7 @@
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
 import android.widget.AdapterView;
 import android.widget.Toast;
 import android.widget.Toolbar;
@@ -824,9 +824,11 @@
     /** The autofill manager. Always access via {@link #getAutofillManager()}. */
     @Nullable private AutofillManager mAutofillManager;
 
-    /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
-    @Nullable private IntelligenceManager mIntelligenceManager;
+    /** The content capture manager. Always access via {@link #getContentCaptureManager()}. */
+    @Nullable private ContentCaptureManager mContentCaptureManager;
 
+    private final ArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
+            new ArrayList<Application.ActivityLifecycleCallbacks>();
 
     static final class NonConfigurationInstances {
         Object activity;
@@ -1014,39 +1016,39 @@
     }
 
     /**
-     * (Creates, sets, and ) returns the intelligence manager
+     * (Creates, sets, and ) returns the content capture manager
      *
-     * @return The intelligence manager
+     * @return The content capture manager
      */
-    @NonNull private IntelligenceManager getIntelligenceManager() {
-        if (mIntelligenceManager == null) {
-            mIntelligenceManager = getSystemService(IntelligenceManager.class);
+    @NonNull private ContentCaptureManager getContentCaptureManager() {
+        if (mContentCaptureManager == null) {
+            mContentCaptureManager = getSystemService(ContentCaptureManager.class);
         }
-        return mIntelligenceManager;
+        return mContentCaptureManager;
     }
 
-    private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
-        final IntelligenceManager im = getIntelligenceManager();
-        if (im == null || !im.isContentCaptureEnabled()) {
+    private void notifyContentCaptureManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+        final ContentCaptureManager cm = getContentCaptureManager();
+        if (cm == null || !cm.isContentCaptureEnabled()) {
             return;
         }
         switch (event) {
             case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
                 //TODO(b/111276913): decide whether the InteractionSessionId should be
                 // saved / restored in the activity bundle.
-                im.onActivityCreated(mToken, getComponentName());
+                cm.onActivityCreated(mToken, getComponentName());
                 break;
             case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
-                im.onActivityDestroyed();
+                cm.onActivityDestroyed();
                 break;
             case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
             case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
             case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
             case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
-                im.onActivityLifecycleEvent(event);
+                cm.onActivityLifecycleEvent(event);
                 break;
             default:
-                Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+                Log.w(TAG, "notifyContentCaptureManagerIfNeeded(): invalid type " + event);
         }
     }
 
@@ -1055,6 +1057,7 @@
         super.attachBaseContext(newBase);
         if (newBase != null) {
             newBase.setAutofillClient(this);
+            newBase.setContentCaptureSupported(true);
         }
     }
 
@@ -1064,6 +1067,294 @@
         return this;
     }
 
+    /** @hide */
+    @Override
+    public boolean isContentCaptureSupported() {
+        return true;
+    }
+
+    /**
+     * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives
+     * lifecycle callbacks for only this Activity.
+     * <p>
+     * In relation to any
+     * {@link Application#registerActivityLifecycleCallbacks Application registered callbacks},
+     * the callbacks registered here will always occur nested within those callbacks. This means:
+     * <ul>
+     *     <li>Pre events will first be sent to Application registered callbacks, then to callbacks
+     *     registered here.</li>
+     *     <li>{@link Application.ActivityLifecycleCallbacks#onActivityCreated(Activity, Bundle)},
+     *     {@link Application.ActivityLifecycleCallbacks#onActivityStarted(Activity)}, and
+     *     {@link Application.ActivityLifecycleCallbacks#onActivityResumed(Activity)} will
+     *     be sent first to Application registered callbacks, then to callbacks registered here.
+     *     For all other events, callbacks registered here will be sent first.</li>
+     *     <li>Post events will first be sent to callbacks registered here, then to
+     *     Application registered callbacks.</li>
+     * </ul>
+     * <p>
+     * If multiple callbacks are registered here, they receive events in a first in (up through
+     * {@link Application.ActivityLifecycleCallbacks#onActivityPostResumed}, last out
+     * ordering.
+     * <p>
+     * It is strongly recommended to register this in the constructor of your Activity to ensure
+     * you get all available callbacks. As this callback is associated with only this Activity,
+     * it is not usually necessary to {@link #unregisterActivityLifecycleCallbacks unregister} it
+     * unless you specifically do not want to receive further lifecycle callbacks.
+     *
+     * @param callback The callback instance to register
+     */
+    public void registerActivityLifecycleCallbacks(
+            @NonNull Application.ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.add(callback);
+        }
+    }
+
+    /**
+     * Unregister an {@link Application.ActivityLifecycleCallbacks} previously registered
+     * with {@link #registerActivityLifecycleCallbacks}. It will not receive any further
+     * callbacks.
+     *
+     * @param callback The callback instance to unregister
+     * @see #registerActivityLifecycleCallbacks
+     */
+    public void unregisterActivityLifecycleCallbacks(
+            @NonNull Application.ActivityLifecycleCallbacks callback) {
+        synchronized (mActivityLifecycleCallbacks) {
+            mActivityLifecycleCallbacks.remove(callback);
+        }
+    }
+
+    private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) {
+        getApplication().dispatchActivityPreCreated(this, savedInstanceState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreCreated(this,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    private void dispatchActivityCreated(@Nullable Bundle savedInstanceState) {
+        getApplication().dispatchActivityCreated(this, savedInstanceState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityCreated(this,
+                        savedInstanceState);
+            }
+        }
+    }
+
+    private void dispatchActivityPostCreated(@Nullable Bundle savedInstanceState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostCreated(this,
+                        savedInstanceState);
+            }
+        }
+        getApplication().dispatchActivityPostCreated(this, savedInstanceState);
+    }
+
+    private void dispatchActivityPreStarted() {
+        getApplication().dispatchActivityPreStarted(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStarted(this);
+            }
+        }
+    }
+
+    private void dispatchActivityStarted() {
+        getApplication().dispatchActivityStarted(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStarted(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPostStarted() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostStarted(this);
+            }
+        }
+        getApplication().dispatchActivityPostStarted(this);
+    }
+
+    private void dispatchActivityPreResumed() {
+        getApplication().dispatchActivityPreResumed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreResumed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityResumed() {
+        getApplication().dispatchActivityResumed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityResumed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPostResumed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = 0; i < callbacks.length; i++) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostResumed(this);
+            }
+        }
+        getApplication().dispatchActivityPostResumed(this);
+    }
+
+    private void dispatchActivityPrePaused() {
+        getApplication().dispatchActivityPrePaused(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPrePaused(this);
+            }
+        }
+    }
+
+    private void dispatchActivityPaused() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPaused(this);
+            }
+        }
+        getApplication().dispatchActivityPaused(this);
+    }
+
+    private void dispatchActivityPostPaused() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPostPaused(this);
+            }
+        }
+        getApplication().dispatchActivityPostPaused(this);
+    }
+
+    private void dispatchActivityPreStopped() {
+        getApplication().dispatchActivityPreStopped(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityPreStopped(this);
+            }
+        }
+    }
+
+    private void dispatchActivityStopped() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityStopped(this);
+            }
+        }
+        getApplication().dispatchActivityStopped(this);
+    }
+
+    private void dispatchActivityPostStopped() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostStopped(this);
+            }
+        }
+        getApplication().dispatchActivityPostStopped(this);
+    }
+
+    private void dispatchActivityPreSaveInstanceState(@NonNull Bundle outState) {
+        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPreSaveInstanceState(this, outState);
+            }
+        }
+    }
+
+    private void dispatchActivitySaveInstanceState(@NonNull Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivitySaveInstanceState(this, outState);
+            }
+        }
+        getApplication().dispatchActivitySaveInstanceState(this, outState);
+    }
+
+    private void dispatchActivityPostSaveInstanceState(@NonNull Bundle outState) {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostSaveInstanceState(this, outState);
+            }
+        }
+        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+    }
+
+    private void dispatchActivityPreDestroyed() {
+        getApplication().dispatchActivityPreDestroyed(this);
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPreDestroyed(this);
+            }
+        }
+    }
+
+    private void dispatchActivityDestroyed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i]).onActivityDestroyed(this);
+            }
+        }
+        getApplication().dispatchActivityDestroyed(this);
+    }
+
+    private void dispatchActivityPostDestroyed() {
+        Object[] callbacks = collectActivityLifecycleCallbacks();
+        if (callbacks != null) {
+            for (int i = callbacks.length - 1; i >= 0; i--) {
+                ((Application.ActivityLifecycleCallbacks) callbacks[i])
+                        .onActivityPostDestroyed(this);
+            }
+        }
+        getApplication().dispatchActivityPostDestroyed(this);
+    }
+
+    private Object[] collectActivityLifecycleCallbacks() {
+        Object[] callbacks = null;
+        synchronized (mActivityLifecycleCallbacks) {
+            if (mActivityLifecycleCallbacks.size() > 0) {
+                callbacks = mActivityLifecycleCallbacks.toArray();
+            }
+        }
+        return callbacks;
+    }
+
     /**
      * Called when the activity is starting.  This is where most initialization
      * should go: calling {@link #setContentView(int)} to inflate the
@@ -1119,14 +1410,14 @@
                     ? mLastNonConfigurationInstances.fragments : null);
         }
         mFragments.dispatchCreate();
-        getApplication().dispatchActivityCreated(this, savedInstanceState);
+        dispatchActivityCreated(savedInstanceState);
         if (mVoiceInteractor != null) {
             mVoiceInteractor.attachActivity(this);
         }
         mRestoredFromBundle = savedInstanceState != null;
         mCalled = true;
 
-        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
+        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
     }
 
     /**
@@ -1355,12 +1646,12 @@
 
         mFragments.doLoaderStart();
 
-        getApplication().dispatchActivityStarted(this);
+        dispatchActivityStarted();
 
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
         }
-        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
+        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
     }
 
     /**
@@ -1426,7 +1717,7 @@
     @CallSuper
     protected void onResume() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
-        getApplication().dispatchActivityResumed(this);
+        dispatchActivityResumed();
         mActivityTransitionState.onResume(this, isTopOfTask());
         enableAutofillCompatibilityIfNeeded();
         if (mAutoFillResetNeeded) {
@@ -1451,7 +1742,7 @@
                 }
             }
         }
-        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
+        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
         mCalled = true;
     }
 
@@ -1642,13 +1933,13 @@
      * @param outState The bundle to save the state to.
      */
     final void performSaveInstanceState(@NonNull Bundle outState) {
-        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        dispatchActivityPreSaveInstanceState(outState);
         onSaveInstanceState(outState);
         saveManagedDialogs(outState);
         mActivityTransitionState.saveState(outState);
         storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
-        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+        dispatchActivityPostSaveInstanceState(outState);
     }
 
     /**
@@ -1662,13 +1953,13 @@
      */
     final void performSaveInstanceState(@NonNull Bundle outState,
             @NonNull PersistableBundle outPersistentState) {
-        getApplication().dispatchActivityPreSaveInstanceState(this, outState);
+        dispatchActivityPreSaveInstanceState(outState);
         onSaveInstanceState(outState, outPersistentState);
         saveManagedDialogs(outState);
         storeHasCurrentPermissionRequest(outState);
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
                 ", " + outPersistentState);
-        getApplication().dispatchActivityPostSaveInstanceState(this, outState);
+        dispatchActivityPostSaveInstanceState(outState);
     }
 
     /**
@@ -1731,7 +2022,7 @@
             outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
             getAutofillManager().onSaveInstanceState(outState);
         }
-        getApplication().dispatchActivitySaveInstanceState(this, outState);
+        dispatchActivitySaveInstanceState(outState);
     }
 
     /**
@@ -1831,7 +2122,7 @@
     @CallSuper
     protected void onPause() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
-        getApplication().dispatchActivityPaused(this);
+        dispatchActivityPaused();
         if (mAutoFillResetNeeded) {
             if (!mAutoFillIgnoreFirstResumePause) {
                 if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
@@ -1845,7 +2136,7 @@
                 mAutoFillIgnoreFirstResumePause = false;
             }
         }
-        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
+        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
         mCalled = true;
     }
 
@@ -2015,7 +2306,7 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
         mActivityTransitionState.onStop();
-        getApplication().dispatchActivityStopped(this);
+        dispatchActivityStopped();
         mTranslucentCallback = null;
         mCalled = true;
 
@@ -2034,7 +2325,7 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
-            notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
+            notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
         }
     }
 
@@ -2104,9 +2395,9 @@
             mActionBar.onDestroy();
         }
 
-        getApplication().dispatchActivityDestroyed(this);
+        dispatchActivityDestroyed();
 
-        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+        notifyContentCaptureManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
 
     }
 
@@ -6522,7 +6813,7 @@
     }
 
     void dumpIntelligenceManager(String prefix, PrintWriter writer) {
-        final IntelligenceManager im = getIntelligenceManager();
+        final ContentCaptureManager im = getContentCaptureManager();
         if (im != null) {
             im.dump(prefix, writer);
         } else {
@@ -7284,7 +7575,7 @@
 
     @UnsupportedAppUsage
     final void performCreate(Bundle icicle, PersistableBundle persistentState) {
-        getApplication().dispatchActivityPreCreated(this, icicle);
+        dispatchActivityPreCreated(icicle);
         mCanEnterPictureInPicture = true;
         restoreHasCurrentPermissionRequest(icicle);
         if (persistentState != null) {
@@ -7299,7 +7590,7 @@
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
         mFragments.dispatchActivityCreated();
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
-        getApplication().dispatchActivityPostCreated(this, icicle);
+        dispatchActivityPostCreated(icicle);
     }
 
     final void performNewIntent(@NonNull Intent intent) {
@@ -7308,7 +7599,7 @@
     }
 
     final void performStart(String reason) {
-        getApplication().dispatchActivityPreStarted(this);
+        dispatchActivityPreStarted();
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
         mFragments.noteStateNotSaved();
         mCalled = false;
@@ -7351,7 +7642,7 @@
         }
 
         mActivityTransitionState.enterReady(this);
-        getApplication().dispatchActivityPostStarted(this);
+        dispatchActivityPostStarted();
     }
 
     /**
@@ -7406,7 +7697,7 @@
     }
 
     final void performResume(boolean followedByPause, String reason) {
-        getApplication().dispatchActivityPreResumed(this);
+        dispatchActivityPreResumed();
         performRestart(true /* start */, reason);
 
         mFragments.execPendingActions();
@@ -7456,11 +7747,11 @@
                 "Activity " + mComponent.toShortString() +
                 " did not call through to super.onPostResume()");
         }
-        getApplication().dispatchActivityPostResumed(this);
+        dispatchActivityPostResumed();
     }
 
     final void performPause() {
-        getApplication().dispatchActivityPrePaused(this);
+        dispatchActivityPrePaused();
         mDoReportFullyDrawn = false;
         mFragments.dispatchPause();
         mCalled = false;
@@ -7473,7 +7764,7 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onPause()");
         }
-        getApplication().dispatchActivityPostPaused(this);
+        dispatchActivityPostPaused();
     }
 
     final void performUserLeaving() {
@@ -7489,7 +7780,7 @@
         mCanEnterPictureInPicture = false;
 
         if (!mStopped) {
-            getApplication().dispatchActivityPreStopped(this);
+            dispatchActivityPreStopped();
             if (mWindow != null) {
                 mWindow.closeAllPanels();
             }
@@ -7524,13 +7815,13 @@
             }
 
             mStopped = true;
-            getApplication().dispatchActivityPostStopped(this);
+            dispatchActivityPostStopped();
         }
         mResumed = false;
     }
 
     final void performDestroy() {
-        getApplication().dispatchActivityPreDestroyed(this);
+        dispatchActivityPreDestroyed();
         mDestroyed = true;
         mWindow.destroy();
         mFragments.dispatchDestroy();
@@ -7540,7 +7831,7 @@
         if (mVoiceInteractor != null) {
             mVoiceInteractor.detachActivity();
         }
-        getApplication().dispatchActivityPostDestroyed(this);
+        dispatchActivityPostDestroyed();
     }
 
     final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1edd7f5..af3da0c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -243,6 +243,8 @@
     public abstract void ensureBootCompleted();
     public abstract void updateOomLevelsForDisplay(int displayId);
     public abstract boolean isActivityStartsLoggingEnabled();
+    /** Returns true if the background activity starts is enabled. */
+    public abstract boolean isBackgroundActivityStartsEnabled();
     public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
 
     /** Input dispatch timeout to a window, start the ANR process. */
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 56ccf6f..6fdf7c8 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -433,4 +433,18 @@
         }
         return sb.toString();
     }
+
+    /**
+     * Clears launch params for the given package.
+     * @param packageNames the names of the packages of which the launch params are to be cleared
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void clearLaunchParamsForPackages(List<String> packageNames) {
+        try {
+            getService().clearLaunchParamsForPackages(packageNames);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index c879db8..2c435a2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -31,10 +31,11 @@
 import android.util.Log;
 import android.view.IWindowManager;
 import android.view.InputDevice;
-import android.view.InputEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -59,12 +60,16 @@
 
     private VirtualDisplay mVirtualDisplay;
     private final SurfaceView mSurfaceView;
-    private Surface mSurface;
+
+    /**
+     * This is the root surface for the VirtualDisplay. The VirtualDisplay child surfaces will be
+     * re-parented to this surface. This will also be a child of the SurfaceView's SurfaceControl.
+     */
+    private SurfaceControl mRootSurfaceControl;
 
     private final SurfaceCallback mSurfaceCallback;
     private StateCallback mActivityViewCallback;
 
-    private IActivityManager mActivityManager;
     private IActivityTaskManager mActivityTaskManager;
     private IInputForwarder mInputForwarder;
     // Temp container to store view coordinates on screen.
@@ -75,6 +80,9 @@
     private final CloseGuard mGuard = CloseGuard.get();
     private boolean mOpened; // Protected by mGuard.
 
+    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+    private Surface mTmpSurface = new Surface();
+
     @UnsupportedAppUsage
     public ActivityView(Context context) {
         this(context, null /* attrs */);
@@ -87,7 +95,6 @@
     public ActivityView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mActivityManager = ActivityManager.getService();
         mActivityTaskManager = ActivityTaskManager.getService();
         mSurfaceView = new SurfaceView(context);
         mSurfaceCallback = new SurfaceCallback();
@@ -283,9 +290,14 @@
         return super.onGenericMotionEvent(event);
     }
 
-    private boolean injectInputEvent(InputEvent event) {
+    private boolean injectInputEvent(MotionEvent event) {
         if (mInputForwarder != null) {
             try {
+                // The touch event that the ActivityView gets is in View space, but the event needs
+                // to get forwarded in screen space. This offsets the touch event by the location
+                // the ActivityView is on screen and sends it to the input forwarder.
+                getLocationOnScreen(mLocationOnScreen);
+                event.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
                 return mInputForwarder.forwardEvent(event);
             } catch (RemoteException e) {
                 e.rethrowAsRuntimeException();
@@ -297,14 +309,19 @@
     private class SurfaceCallback implements SurfaceHolder.Callback {
         @Override
         public void surfaceCreated(SurfaceHolder surfaceHolder) {
-            mSurface = mSurfaceView.getHolder().getSurface();
+            mTmpSurface = new Surface();
             if (mVirtualDisplay == null) {
-                initVirtualDisplay();
+                initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
                 if (mVirtualDisplay != null && mActivityViewCallback != null) {
                     mActivityViewCallback.onActivityViewReady(ActivityView.this);
                 }
             } else {
-                mVirtualDisplay.setSurface(surfaceHolder.getSurface());
+                // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+                // whether it has a surface. Setting a fake surface here so DisplayManager will
+                // consider this display on.
+                mVirtualDisplay.setSurface(mTmpSurface);
+                mTmpTransaction.reparent(mRootSurfaceControl,
+                        mSurfaceView.getSurfaceControl().getHandle()).apply();
             }
             updateLocation();
         }
@@ -319,8 +336,8 @@
 
         @Override
         public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
-            mSurface.release();
-            mSurface = null;
+            mTmpSurface.release();
+            mTmpSurface = null;
             if (mVirtualDisplay != null) {
                 mVirtualDisplay.setSurface(null);
             }
@@ -328,7 +345,7 @@
         }
     }
 
-    private void initVirtualDisplay() {
+    private void initVirtualDisplay(SurfaceSession surfaceSession) {
         if (mVirtualDisplay != null) {
             throw new IllegalStateException("Trying to initialize for the second time.");
         }
@@ -336,9 +353,13 @@
         final int width = mSurfaceView.getWidth();
         final int height = mSurfaceView.getHeight();
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+        // TODO (b/119209373): DisplayManager determines if a VirtualDisplay is on by
+        // whether it has a surface. Setting a fake surface here so DisplayManager will consider
+        // this display on.
         mVirtualDisplay = displayManager.createVirtualDisplay(
                 DISPLAY_NAME + "@" + System.identityHashCode(this),
-                width, height, getBaseDisplayDensity(), mSurface,
+                width, height, getBaseDisplayDensity(), mTmpSurface,
                 DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
                         | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
         if (mVirtualDisplay == null) {
@@ -348,11 +369,20 @@
 
         final int displayId = mVirtualDisplay.getDisplay().getDisplayId();
         final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+        mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
+                .setContainerLayer(true)
+                .setName(DISPLAY_NAME)
+                .build();
+
         try {
+            wm.reparentDisplayContent(displayId, mRootSurfaceControl.getHandle());
             wm.dontOverrideDisplayInfo(displayId);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+
+        mTmpTransaction.show(mRootSurfaceControl).apply();
         mInputForwarder = InputManager.getInstance().createInputForwarder(displayId);
         mTaskStackListener = new TaskStackListenerImpl();
         try {
@@ -392,9 +422,9 @@
             displayReleased = false;
         }
 
-        if (mSurface != null) {
-            mSurface.release();
-            mSurface = null;
+        if (mTmpSurface != null) {
+            mTmpSurface.release();
+            mTmpSurface = null;
         }
 
         if (displayReleased && mActivityViewCallback != null) {
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9b13420..2f0f14aa 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -35,7 +35,7 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
diff --git a/core/java/android/app/AppDetailsActivity.java b/core/java/android/app/AppDetailsActivity.java
index cd36e63..b71af88 100644
--- a/core/java/android/app/AppDetailsActivity.java
+++ b/core/java/android/app/AppDetailsActivity.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.TestApi;
 import android.content.Intent;
 import android.os.Bundle;
 
@@ -24,7 +25,9 @@
  *
  * @hide
  */
+@TestApi
 public class AppDetailsActivity extends Activity {
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 8bb704d..7312b2c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2046,8 +2046,6 @@
             StorageManager storage) {
         if (app.isInternal()) {
             return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
-        } else if (app.isExternalAsec()) {
-            return storage.getPrimaryPhysicalVolume();
         } else {
             return storage.findVolumeByUuid(app.volumeUuid);
         }
@@ -2983,4 +2981,13 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public void sendDeviceCustomizationReadyBroadcast() {
+        try {
+            mPM.sendDeviceCustomizationReadyBroadcast();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 28ecb27..6f0b6c8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -217,6 +217,8 @@
     private AutofillClient mAutofillClient = null;
     private boolean mIsAutofillCompatEnabled;
 
+    private boolean mIsContentCaptureSupported = false;
+
     private final Object mSync = new Object();
 
     @GuardedBy("mSync")
@@ -2376,6 +2378,18 @@
         mIsAutofillCompatEnabled = autofillCompatEnabled;
     }
 
+    /** @hide */
+    @Override
+    public boolean isContentCaptureSupported() {
+        return mIsContentCaptureSupported;
+    }
+
+    /** @hide */
+    @Override
+    public void setContentCaptureSupported(boolean supported) {
+        mIsContentCaptureSupported = supported;
+    }
+
     @UnsupportedAppUsage
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e83bcd0..88fb025 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -96,6 +96,7 @@
             String callingPackage);
     void unregisterUidObserver(in IUidObserver observer);
     boolean isUidActive(int uid, String callingPackage);
+    int getUidProcessState(int uid, in String callingPackage);
     // =============== End of transactions used on native side as well ============================
 
     // Special low-level communication with activity manager.
@@ -379,8 +380,6 @@
     void noteAlarmFinish(in IIntentSender sender, in WorkSource workSource, int sourceUid, in String tag);
     int getPackageProcessState(in String packageName, in String callingPackage);
     void updateDeviceOwner(in String packageName);
-    int getUidProcessState(int uid, in String callingPackage);
-
 
     // Start of N transactions
     // Start Binder transaction tracking for all applications.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 09b77d5..777a494 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -445,4 +445,9 @@
     void setPackageScreenCompatMode(in String packageName, int mode);
     boolean getPackageAskScreenCompat(in String packageName);
     void setPackageAskScreenCompat(in String packageName, boolean ask);
+
+    /**
+     * Clears launch params for given packages.
+     */
+    void clearLaunchParamsForPackages(in List<String> packageNames);
 }
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index ce88809..e116d98 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -43,8 +43,6 @@
      */
     void onUidIdle(int uid, boolean disabled);
 
-    // =============== End of transactions used on native side as well ============================
-
     /**
      * General report of a state change of an uid.
      *
@@ -55,6 +53,8 @@
      */
     void onUidStateChanged(int uid, int procState, long procStateSeq);
 
+    // =============== End of transactions used on native side as well ============================
+
     /**
      * Report when the cached state of a uid has changed.
      * If true, a uid has become cached -- that is, it has some active processes that are
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 00547b4..3a2038d 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -159,5 +159,5 @@
     /**
      * Called from SystemUI when it shows the AoD UI.
      */
-    oneway void setInAmbientMode(boolean inAmbientMode, boolean animated);
+    oneway void setInAmbientMode(boolean inAmbientMode, long animationDuration);
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3f10754..719bba0 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -696,6 +696,23 @@
         return loaders;
     }
 
+    private StrictMode.ThreadPolicy allowThreadDiskReads() {
+        if (mActivityThread == null) {
+            // When LoadedApk is used without an ActivityThread (usually in a
+            // zygote context), don't call into StrictMode, as it initializes
+            // the binder subsystem, which we don't want.
+            return null;
+        }
+
+        return StrictMode.allowThreadDiskReads();
+    }
+
+    private void setThreadPolicy(StrictMode.ThreadPolicy policy) {
+        if (mActivityThread != null && policy != null) {
+            StrictMode.setThreadPolicy(policy);
+        }
+    }
+
     private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
         if (mPackageName.equals("android")) {
             // Note: This branch is taken for system server and we don't need to setup
@@ -718,8 +735,11 @@
 
         // Avoid the binder call when the package is the current application package.
         // The activity manager will perform ensure that dexopt is performed before
-        // spinning up the process.
-        if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
+        // spinning up the process. Similarly, don't call into binder when we don't
+        // have an ActivityThread object.
+        if (mActivityThread != null
+                && !Objects.equals(mPackageName, ActivityThread.currentPackageName())
+                && mIncludeCode) {
             try {
                 ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
                         PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
@@ -790,12 +810,12 @@
         // mIncludeCode == false).
         if (!mIncludeCode) {
             if (mDefaultClassLoader == null) {
-                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+                StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
                 mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                         "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                         librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                         null /* classLoaderName */);
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
                 mAppComponentFactory = AppComponentFactory.DEFAULT;
             }
 
@@ -822,7 +842,7 @@
         if (mDefaultClassLoader == null) {
             // Temporarily disable logging of disk reads on the Looper thread
             // as this is early and necessary.
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
 
             List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
                     mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
@@ -834,18 +854,18 @@
                     mApplicationInfo.classLoaderName, sharedLibraries);
             mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
 
-            StrictMode.setThreadPolicy(oldPolicy);
+            setThreadPolicy(oldPolicy);
             // Setup the class loader paths for profiling.
             needToSetupJitProfiles = true;
         }
 
         if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
             // Temporarily disable logging of disk reads on the Looper thread as this is necessary
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
             try {
                 ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
             } finally {
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
             }
         }
 
@@ -879,11 +899,11 @@
             extraLibPaths.add("/product/lib" + abiSuffix);
         }
         if (!extraLibPaths.isEmpty()) {
-            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
             try {
                 ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
             } finally {
-                StrictMode.setThreadPolicy(oldPolicy);
+                setThreadPolicy(oldPolicy);
             }
         }
 
diff --git a/core/java/android/app/Notification.aidl b/core/java/android/app/Notification.aidl
index 9d8129c..8a7156e 100644
--- a/core/java/android/app/Notification.aidl
+++ b/core/java/android/app/Notification.aidl
@@ -17,3 +17,4 @@
 package android.app;
 
 parcelable Notification;
+parcelable Notification.Action;
\ No newline at end of file
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75b56f3..aa1b5af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1693,11 +1693,30 @@
             }
 
             /**
+             * Throws an NPE if we are building a contextual action missing one of the fields
+             * necessary to display the action.
+             */
+            private void checkContextualActionNullFields() {
+                if (mSemanticAction != SEMANTIC_ACTION_CONTEXTUAL_SUGGESTION) return;
+
+                if (mIcon == null) {
+                    throw new NullPointerException("Contextual Actions must contain a valid icon");
+                }
+
+                if (mIntent == null) {
+                    throw new NullPointerException(
+                            "Contextual Actions must contain a valid PendingIntent");
+                }
+            }
+
+            /**
              * Combine all of the options that have been set and return a new {@link Action}
              * object.
              * @return the built action
              */
             public Action build() {
+                checkContextualActionNullFields();
+
                 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
                 RemoteInput[] previousDataInputs =
                     (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
@@ -3848,6 +3867,9 @@
          * The system UI may choose to display a heads-up notification, instead of
          * launching this intent, while the user is using the device.
          * </p>
+         * <p>Apps targeting {@link Build.VERSION_CODES#Q} and above will have to request
+         * a permission ({@link android.Manifest.permission#USE_FULL_SCREEN_INTENT}) in order to
+         * use full screen intents.</p>
          *
          * @param intent The pending intent to launch.
          * @param highPriority Passing true will cause this notification to be sent
@@ -4436,19 +4458,27 @@
             return bitmap;
         }
 
-        private void bindProfileBadge(RemoteViews contentView) {
+        private void bindProfileBadge(RemoteViews contentView, StandardTemplateParams p) {
             Bitmap profileBadge = getProfileBadge();
 
             if (profileBadge != null) {
                 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
                 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
-                if (isColorized()) {
+                if (isColorized(p)) {
                     contentView.setDrawableTint(R.id.profile_badge, false,
-                            getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
+                            getPrimaryTextColor(p), PorterDuff.Mode.SRC_ATOP);
                 }
             }
         }
 
+        private void bindAlertedIcon(RemoteViews contentView, StandardTemplateParams p) {
+            contentView.setDrawableTint(
+                    R.id.alerted_icon,
+                    false /* targetBackground */,
+                    getNeutralColor(p),
+                    PorterDuff.Mode.SRC_ATOP);
+        }
+
         /**
          * @hide
          */
@@ -4507,16 +4537,6 @@
                     result);
         }
 
-        /**
-         * @param hasProgress whether the progress bar should be shown and set
-         * @param result
-         */
-        private RemoteViews applyStandardTemplate(int resId, boolean hasProgress,
-                TemplateBindResult result) {
-            return applyStandardTemplate(resId, mParams.reset().hasProgress(hasProgress)
-                    .fillTextsFrom(this), result);
-        }
-
         private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
                 TemplateBindResult result) {
             RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -4524,15 +4544,15 @@
             resetStandardTemplate(contentView);
 
             final Bundle ex = mN.extras;
-            updateBackgroundColor(contentView);
-            bindNotificationHeader(contentView, p.ambient, p.headerTextSecondary);
+            updateBackgroundColor(contentView, p);
+            bindNotificationHeader(contentView, p);
             bindLargeIconAndReply(contentView, p, result);
-            boolean showProgress = handleProgressBar(p.hasProgress, contentView, ex);
+            boolean showProgress = handleProgressBar(contentView, ex, p);
             if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
                 if (!p.ambient) {
-                    setTextViewColorPrimary(contentView, R.id.title);
+                    setTextViewColorPrimary(contentView, R.id.title, p);
                 }
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
@@ -4543,7 +4563,7 @@
                         : com.android.internal.R.id.text;
                 contentView.setTextViewText(textId, processTextSpans(p.text));
                 if (!p.ambient) {
-                    setTextViewColorSecondary(contentView, textId);
+                    setTextViewColorSecondary(contentView, textId, p);
                 }
                 contentView.setViewVisibility(textId, View.VISIBLE);
             }
@@ -4560,8 +4580,9 @@
             return text;
         }
 
-        private void setTextViewColorPrimary(RemoteViews contentView, int id) {
-            ensureColors();
+        private void setTextViewColorPrimary(RemoteViews contentView, int id,
+                StandardTemplateParams p) {
+            ensureColors(p);
             contentView.setTextColor(id, mPrimaryTextColor);
         }
 
@@ -4570,42 +4591,63 @@
         }
 
         /**
-         * @return the primary text color
+         * Return the primary text color using the existing template params
          * @hide
          */
         @VisibleForTesting
         public int getPrimaryTextColor() {
-            ensureColors();
+            return getPrimaryTextColor(mParams);
+        }
+
+        /**
+         * @param p the template params to inflate this with
+         * @return the primary text color
+         * @hide
+         */
+        @VisibleForTesting
+        public int getPrimaryTextColor(StandardTemplateParams p) {
+            ensureColors(p);
             return mPrimaryTextColor;
         }
 
         /**
-         * @return the secondary text color
+         * Return the secondary text color using the existing template params
          * @hide
          */
         @VisibleForTesting
         public int getSecondaryTextColor() {
-            ensureColors();
+            return getSecondaryTextColor(mParams);
+        }
+
+        /**
+         * @param p the template params to inflate this with
+         * @return the secondary text color
+         * @hide
+         */
+        @VisibleForTesting
+        public int getSecondaryTextColor(StandardTemplateParams p) {
+            ensureColors(p);
             return mSecondaryTextColor;
         }
 
-        private void setTextViewColorSecondary(RemoteViews contentView, int id) {
-            ensureColors();
+        private void setTextViewColorSecondary(RemoteViews contentView, int id,
+                StandardTemplateParams p) {
+            ensureColors(p);
             contentView.setTextColor(id, mSecondaryTextColor);
         }
 
-        private void ensureColors() {
-            int backgroundColor = getBackgroundColor();
+        private void ensureColors(StandardTemplateParams p) {
+            int backgroundColor = getBackgroundColor(p);
             if (mPrimaryTextColor == COLOR_INVALID
                     || mSecondaryTextColor == COLOR_INVALID
                     || mTextColorsAreForBackground != backgroundColor) {
                 mTextColorsAreForBackground = backgroundColor;
-                if (!hasForegroundColor() || !isColorized()) {
+                if (!hasForegroundColor() || !isColorized(p)) {
                     mPrimaryTextColor = ContrastColorUtil.resolvePrimaryColor(mContext,
                             backgroundColor, mInNightMode);
                     mSecondaryTextColor = ContrastColorUtil.resolveSecondaryColor(mContext,
                             backgroundColor, mInNightMode);
-                    if (backgroundColor != COLOR_DEFAULT && isColorized()) {
+                    if (backgroundColor != COLOR_DEFAULT && isColorized(p)) {
                         mPrimaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
                                 mPrimaryTextColor, backgroundColor, 4.5);
                         mSecondaryTextColor = ContrastColorUtil.findAlphaToMeetContrast(
@@ -4673,10 +4715,11 @@
             }
         }
 
-        private void updateBackgroundColor(RemoteViews contentView) {
-            if (isColorized()) {
+        private void updateBackgroundColor(RemoteViews contentView,
+                StandardTemplateParams p) {
+            if (isColorized(p)) {
                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundColor",
-                        getBackgroundColor());
+                        getBackgroundColor(p));
             } else {
                 // Clear it!
                 contentView.setInt(R.id.status_bar_latest_event_content, "setBackgroundResource",
@@ -4699,19 +4742,20 @@
             remoteView.setInt(R.id.notification_main_column, "setMinimumHeight", minHeight);
         }
 
-        private boolean handleProgressBar(boolean hasProgress, RemoteViews contentView, Bundle ex) {
+        private boolean handleProgressBar(RemoteViews contentView, Bundle ex,
+                StandardTemplateParams p) {
             final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
             final int progress = ex.getInt(EXTRA_PROGRESS, 0);
             final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
-            if (hasProgress && (max != 0 || ind)) {
+            if (p.hasProgress && (max != 0 || ind)) {
                 contentView.setViewVisibility(com.android.internal.R.id.progress, View.VISIBLE);
                 contentView.setProgressBar(
                         R.id.progress, max, progress, ind);
                 contentView.setProgressBackgroundTintList(
                         R.id.progress, ColorStateList.valueOf(mContext.getColor(
                                 R.color.notification_progress_background_color)));
-                if (mN.color != COLOR_DEFAULT) {
-                    ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor());
+                if (getRawColor(p) != COLOR_DEFAULT) {
+                    ColorStateList colorStateList = ColorStateList.valueOf(resolveContrastColor(p));
                     contentView.setProgressTintList(R.id.progress, colorStateList);
                     contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
                 }
@@ -4724,8 +4768,8 @@
 
         private void bindLargeIconAndReply(RemoteViews contentView, StandardTemplateParams p,
                 TemplateBindResult result) {
-            boolean largeIconShown = bindLargeIcon(contentView, p.hideLargeIcon || p.ambient);
-            boolean replyIconShown = bindReplyIcon(contentView, p.hideReplyIcon || p.ambient);
+            boolean largeIconShown = bindLargeIcon(contentView, p);
+            boolean replyIconShown = bindReplyIcon(contentView, p);
             contentView.setViewVisibility(R.id.right_icon_container,
                     largeIconShown || replyIconShown ? View.VISIBLE : View.GONE);
             int marginEnd = calculateMarginEnd(largeIconShown, replyIconShown);
@@ -4773,15 +4817,15 @@
          * Bind the large icon.
          * @return if the largeIcon is visible
          */
-        private boolean bindLargeIcon(RemoteViews contentView, boolean hideLargeIcon) {
+        private boolean bindLargeIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mLargeIcon == null && mN.largeIcon != null) {
                 mN.mLargeIcon = Icon.createWithBitmap(mN.largeIcon);
             }
-            boolean showLargeIcon = mN.mLargeIcon != null && !hideLargeIcon;
+            boolean showLargeIcon = mN.mLargeIcon != null && !p.hideLargeIcon && !p.ambient;
             if (showLargeIcon) {
                 contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
                 contentView.setImageViewIcon(R.id.right_icon, mN.mLargeIcon);
-                processLargeLegacyIcon(mN.mLargeIcon, contentView);
+                processLargeLegacyIcon(mN.mLargeIcon, contentView, p);
             }
             return showLargeIcon;
         }
@@ -4790,8 +4834,8 @@
          * Bind the reply icon.
          * @return if the reply icon is visible
          */
-        private boolean bindReplyIcon(RemoteViews contentView, boolean hideReplyIcon) {
-            boolean actionVisible = !hideReplyIcon;
+        private boolean bindReplyIcon(RemoteViews contentView, StandardTemplateParams p) {
+            boolean actionVisible = !p.hideReplyIcon && !p.ambient;
             Action action = null;
             if (actionVisible) {
                 action = findReplyAction();
@@ -4801,7 +4845,7 @@
                 contentView.setViewVisibility(R.id.reply_icon_action, View.VISIBLE);
                 contentView.setDrawableTint(R.id.reply_icon_action,
                         false /* targetBackground */,
-                        getNeutralColor(),
+                        getNeutralColor(p),
                         PorterDuff.Mode.SRC_ATOP);
                 contentView.setOnClickPendingIntent(R.id.reply_icon_action, action.actionIntent);
                 contentView.setRemoteInputs(R.id.reply_icon_action, action.mRemoteInputs);
@@ -4828,41 +4872,42 @@
             return null;
         }
 
-        private void bindNotificationHeader(RemoteViews contentView, boolean ambient,
-                CharSequence secondaryHeaderText) {
-            bindSmallIcon(contentView, ambient);
-            bindHeaderAppName(contentView, ambient);
-            if (!ambient) {
+        private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
+            bindSmallIcon(contentView, p);
+            bindHeaderAppName(contentView, p);
+            if (!p.ambient) {
                 // Ambient view does not have these
-                bindHeaderText(contentView);
-                bindHeaderTextSecondary(contentView, secondaryHeaderText);
-                bindHeaderChronometerAndTime(contentView);
-                bindProfileBadge(contentView);
+                bindHeaderText(contentView, p);
+                bindHeaderTextSecondary(contentView, p);
+                bindHeaderChronometerAndTime(contentView, p);
+                bindProfileBadge(contentView, p);
+                bindAlertedIcon(contentView, p);
             }
-            bindActivePermissions(contentView, ambient);
-            bindExpandButton(contentView);
+            bindActivePermissions(contentView, p);
+            bindExpandButton(contentView, p);
             mN.mUsesStandardHeader = true;
         }
 
-        private void bindActivePermissions(RemoteViews contentView, boolean ambient) {
-            int color = ambient ? resolveAmbientColor() : getNeutralColor();
+        private void bindActivePermissions(RemoteViews contentView, StandardTemplateParams p) {
+            int color = p.ambient ? resolveAmbientColor(p) : getNeutralColor(p);
             contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
             contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
         }
 
-        private void bindExpandButton(RemoteViews contentView) {
-            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+        private void bindExpandButton(RemoteViews contentView, StandardTemplateParams p) {
+            int color = isColorized(p) ? getPrimaryTextColor(p) : getSecondaryTextColor(p);
             contentView.setDrawableTint(R.id.expand_button, false, color,
                     PorterDuff.Mode.SRC_ATOP);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
                     color);
         }
 
-        private void bindHeaderChronometerAndTime(RemoteViews contentView) {
+        private void bindHeaderChronometerAndTime(RemoteViews contentView,
+                StandardTemplateParams p) {
             if (showsTimeOrChronometer()) {
                 contentView.setViewVisibility(R.id.time_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.time_divider);
+                setTextViewColorSecondary(contentView, R.id.time_divider, p);
                 if (mN.extras.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
                     contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
                     contentView.setLong(R.id.chronometer, "setBase",
@@ -4870,11 +4915,11 @@
                     contentView.setBoolean(R.id.chronometer, "setStarted", true);
                     boolean countsDown = mN.extras.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN);
                     contentView.setChronometerCountDown(R.id.chronometer, countsDown);
-                    setTextViewColorSecondary(contentView, R.id.chronometer);
+                    setTextViewColorSecondary(contentView, R.id.chronometer, p);
                 } else {
                     contentView.setViewVisibility(R.id.time, View.VISIBLE);
                     contentView.setLong(R.id.time, "setTime", mN.when);
-                    setTextViewColorSecondary(contentView, R.id.time);
+                    setTextViewColorSecondary(contentView, R.id.time, p);
                 }
             } else {
                 // We still want a time to be set but gone, such that we can show and hide it
@@ -4883,36 +4928,36 @@
             }
         }
 
-        private void bindHeaderText(RemoteViews contentView) {
-            CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
+        private void bindHeaderText(RemoteViews contentView, StandardTemplateParams p) {
+            CharSequence summaryText = p.summaryText;
+            if (summaryText == null && mStyle != null && mStyle.mSummaryTextSet
                     && mStyle.hasSummaryInHeader()) {
-                headerText = mStyle.mSummaryText;
+                summaryText = mStyle.mSummaryText;
             }
-            if (headerText == null
+            if (summaryText == null
                     && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
-                headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+                summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
             }
-            if (headerText != null) {
+            if (summaryText != null) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processTextSpans(
-                        processLegacyText(headerText)));
-                setTextViewColorSecondary(contentView, R.id.header_text);
+                        processLegacyText(summaryText)));
+                setTextViewColorSecondary(contentView, R.id.header_text, p);
                 contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
                 contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.header_text_divider);
+                setTextViewColorSecondary(contentView, R.id.header_text_divider, p);
             }
         }
 
-        private void bindHeaderTextSecondary(RemoteViews contentView, CharSequence secondaryText) {
-            if (!TextUtils.isEmpty(secondaryText)) {
+        private void bindHeaderTextSecondary(RemoteViews contentView, StandardTemplateParams p) {
+            if (!TextUtils.isEmpty(p.headerTextSecondary)) {
                 contentView.setTextViewText(R.id.header_text_secondary, processTextSpans(
-                        processLegacyText(secondaryText)));
-                setTextViewColorSecondary(contentView, R.id.header_text_secondary);
+                        processLegacyText(p.headerTextSecondary)));
+                setTextViewColorSecondary(contentView, R.id.header_text_secondary, p);
                 contentView.setViewVisibility(R.id.header_text_secondary, View.VISIBLE);
                 contentView.setViewVisibility(R.id.header_text_secondary_divider, View.VISIBLE);
-                setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider);
+                setTextViewColorSecondary(contentView, R.id.header_text_secondary_divider, p);
             }
         }
 
@@ -4950,23 +4995,27 @@
 
             return String.valueOf(name);
         }
-        private void bindHeaderAppName(RemoteViews contentView, boolean ambient) {
+        private void bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
             contentView.setTextViewText(R.id.app_name_text, loadHeaderAppName());
-            if (isColorized() && !ambient) {
-                setTextViewColorPrimary(contentView, R.id.app_name_text);
+            if (isColorized(p)) {
+                setTextViewColorPrimary(contentView, R.id.app_name_text, p);
             } else {
                 contentView.setTextColor(R.id.app_name_text,
-                        ambient ? resolveAmbientColor() : getSecondaryTextColor());
+                        p.ambient ? resolveAmbientColor(p) : getSecondaryTextColor(p));
             }
         }
 
-        private void bindSmallIcon(RemoteViews contentView, boolean ambient) {
+        private boolean isColorized(StandardTemplateParams p) {
+            return p.allowColorization && !p.ambient && mN.isColorized();
+        }
+
+        private void bindSmallIcon(RemoteViews contentView, StandardTemplateParams p) {
             if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
             contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
-            processSmallIconColor(mN.mSmallIcon, contentView, ambient);
+            processSmallIconColor(mN.mSmallIcon, contentView, p);
         }
 
         /**
@@ -5041,8 +5090,7 @@
                     boolean actionHasValidInput = hasValidRemoteInput(action);
                     validRemoteInput |= actionHasValidInput;
 
-                    final RemoteViews button = generateActionButton(action, emphazisedMode,
-                            p.ambient);
+                    final RemoteViews button = generateActionButton(action, emphazisedMode, p);
                     if (actionHasValidInput && !emphazisedMode) {
                         // Clear the drawable
                         button.setInt(R.id.action0, "setBackgroundResource", 0);
@@ -5063,20 +5111,20 @@
                         View.VISIBLE);
                 big.setTextViewText(R.id.notification_material_reply_text_1,
                         processTextSpans(replyText[0]));
-                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1);
+                setTextViewColorSecondary(big, R.id.notification_material_reply_text_1, p);
                 big.setViewVisibility(R.id.notification_material_reply_progress,
                         showSpinner ? View.VISIBLE : View.GONE);
                 big.setProgressIndeterminateTintList(
                         R.id.notification_material_reply_progress,
                         ColorStateList.valueOf(
-                                isColorized() ? getPrimaryTextColor() : resolveContrastColor()));
+                                isColorized(p) ? getPrimaryTextColor(p) : resolveContrastColor(p)));
 
                 if (replyText.length > 1 && !TextUtils.isEmpty(replyText[1])
                         && p.maxRemoteInputHistory > 1) {
                     big.setViewVisibility(R.id.notification_material_reply_text_2, View.VISIBLE);
                     big.setTextViewText(R.id.notification_material_reply_text_2,
                             processTextSpans(replyText[1]));
-                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2);
+                    setTextViewColorSecondary(big, R.id.notification_material_reply_text_2, p);
 
                     if (replyText.length > 2 && !TextUtils.isEmpty(replyText[2])
                             && p.maxRemoteInputHistory > 2) {
@@ -5084,7 +5132,7 @@
                                 R.id.notification_material_reply_text_3, View.VISIBLE);
                         big.setTextViewText(R.id.notification_material_reply_text_3,
                                 processTextSpans(replyText[2]));
-                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3);
+                        setTextViewColorSecondary(big, R.id.notification_material_reply_text_3, p);
                     }
                 }
             }
@@ -5175,18 +5223,23 @@
          * @hide
          */
         public RemoteViews makeNotificationHeader(boolean ambient) {
-            Boolean colorized = (Boolean) mN.extras.get(EXTRA_COLORIZED);
-            mN.extras.putBoolean(EXTRA_COLORIZED, false);
+            return makeNotificationHeader(mParams.reset().ambient(ambient).fillTextsFrom(this));
+        }
+
+        /**
+         * Construct a RemoteViews for the final notification header only. This will not be
+         * colorized.
+         *
+         * @param p the template params to inflate this with
+         */
+        private RemoteViews makeNotificationHeader(StandardTemplateParams p) {
+            // Headers on their own are never colorized
+            p.disallowColorization();
             RemoteViews header = new BuilderRemoteViews(mContext.getApplicationInfo(),
-                    ambient ? R.layout.notification_template_ambient_header
+                    p.ambient ? R.layout.notification_template_ambient_header
                             : R.layout.notification_template_header);
             resetNotificationHeader(header);
-            bindNotificationHeader(header, ambient, null);
-            if (colorized != null) {
-                mN.extras.putBoolean(EXTRA_COLORIZED, colorized);
-            } else {
-                mN.extras.remove(EXTRA_COLORIZED);
-            }
+            bindNotificationHeader(header, p);
             return header;
         }
 
@@ -5329,24 +5382,15 @@
          * @hide
          */
         public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
-            int color = mN.color;
-            mN.color = COLOR_DEFAULT;
-            CharSequence summary = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            if (!useRegularSubtext || TextUtils.isEmpty(summary)) {
-                CharSequence newSummary = createSummaryText();
-                if (!TextUtils.isEmpty(newSummary)) {
-                    mN.extras.putCharSequence(EXTRA_SUB_TEXT, newSummary);
-                }
+            StandardTemplateParams p = mParams.reset()
+                    .forceDefaultColor()
+                    .ambient(false)
+                    .fillTextsFrom(this);
+            if (!useRegularSubtext || TextUtils.isEmpty(mParams.summaryText)) {
+                p.summaryText(createSummaryText());
             }
-
-            RemoteViews header = makeNotificationHeader(false /* ambient */);
+            RemoteViews header = makeNotificationHeader(p);
             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);
-            }
-            mN.color = color;
             return header;
         }
 
@@ -5375,7 +5419,7 @@
         }
 
         private RemoteViews generateActionButton(Action action, boolean emphazisedMode,
-                boolean ambient) {
+                StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             RemoteViews button = new BuilderRemoteViews(mContext.getApplicationInfo(),
                     emphazisedMode ? getEmphasizedActionLayoutResource()
@@ -5392,7 +5436,7 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = null;
-                int background = resolveBackgroundColor();
+                int background = resolveBackgroundColor(p);
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
@@ -5400,7 +5444,7 @@
                     title = ensureColorSpanContrast(title, background, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
-                setTextViewColorPrimary(button, R.id.action0);
+                setTextViewColorPrimary(button, R.id.action0, p);
                 int rippleColor;
                 boolean hasColorOverride = outResultColor != null && outResultColor[0] != null;
                 if (hasColorOverride) {
@@ -5411,11 +5455,12 @@
                             background, mInNightMode);
                     button.setTextColor(R.id.action0, textColor);
                     rippleColor = textColor;
-                } else if (mN.color != COLOR_DEFAULT && !isColorized() && mTintActionButtons) {
-                    rippleColor = resolveContrastColor();
+                } else if (getRawColor(p) != COLOR_DEFAULT && !isColorized(p)
+                        && mTintActionButtons) {
+                    rippleColor = resolveContrastColor(p);
                     button.setTextColor(R.id.action0, rippleColor);
                 } else {
-                    rippleColor = getPrimaryTextColor();
+                    rippleColor = getPrimaryTextColor(p);
                 }
                 // We only want about 20% alpha for the ripple
                 rippleColor = (rippleColor & 0x00ffffff) | 0x33000000;
@@ -5427,13 +5472,15 @@
             } else {
                 button.setTextViewText(R.id.action0, processTextSpans(
                         processLegacyText(action.title)));
-                if (isColorized() && !ambient) {
-                    setTextViewColorPrimary(button, R.id.action0);
-                } else if (mN.color != COLOR_DEFAULT && mTintActionButtons) {
+                if (isColorized(p)) {
+                    setTextViewColorPrimary(button, R.id.action0, p);
+                } else if (getRawColor(p) != COLOR_DEFAULT && mTintActionButtons) {
                     button.setTextColor(R.id.action0,
-                            ambient ? resolveAmbientColor() : resolveContrastColor());
+                            p.ambient ? resolveAmbientColor(p) : resolveContrastColor(p));
                 }
             }
+            button.setIntTag(R.id.action0, R.id.notification_action_index_tag,
+                    mActions.indexOf(action));
             return button;
         }
 
@@ -5539,15 +5586,15 @@
          * Apply any necessariy colors to the small icon
          */
         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView,
-                boolean ambient) {
+                StandardTemplateParams p) {
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
             int color;
-            if (ambient) {
-                color = resolveAmbientColor();
-            } else if (isColorized()) {
-                color = getPrimaryTextColor();
+            if (p.ambient) {
+                color = resolveAmbientColor(p);
+            } else if (isColorized(p)) {
+                color = getPrimaryTextColor(p);
             } else {
-                color = resolveContrastColor();
+                color = resolveContrastColor(p);
             }
             if (colorable) {
                 contentView.setDrawableTint(R.id.icon, false, color,
@@ -5563,11 +5610,12 @@
          * if it's grayscale).
          */
         // TODO: also check bounds, transparency, that sort of thing.
-        private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
+        private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView,
+                StandardTemplateParams p) {
             if (largeIcon != null && isLegacy()
                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
                 // resolve color will fall back to the default when legacy
-                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(p),
                         PorterDuff.Mode.SRC_ATOP);
             }
         }
@@ -5578,29 +5626,43 @@
             }
         }
 
-        int resolveContrastColor() {
-            if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
+        int resolveContrastColor(StandardTemplateParams p) {
+            int rawColor = getRawColor(p);
+            if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
                 return mCachedContrastColor;
             }
 
             int color;
             int background = mContext.getColor(
                     com.android.internal.R.color.notification_material_background_color);
-            if (mN.color == COLOR_DEFAULT) {
-                ensureColors();
+            if (rawColor == COLOR_DEFAULT) {
+                ensureColors(p);
                 color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode);
             } else {
-                color = ContrastColorUtil.resolveContrastColor(mContext, mN.color,
+                color = ContrastColorUtil.resolveContrastColor(mContext, rawColor,
                         background, mInNightMode);
             }
             if (Color.alpha(color) < 255) {
                 // alpha doesn't go well for color filters, so let's blend it manually
                 color = ContrastColorUtil.compositeColors(color, background);
             }
-            mCachedContrastColorIsFor = mN.color;
+            mCachedContrastColorIsFor = rawColor;
             return mCachedContrastColor = color;
         }
 
+        /**
+         * Return the raw color of this Notification, which doesn't necessarily satisfy contrast.
+         *
+         * @see #resolveContrastColor(StandardTemplateParams) for the contrasted color
+         * @param p the template params to inflate this with
+         */
+        private int getRawColor(StandardTemplateParams p) {
+            if (p.forceDefaultColor) {
+                return COLOR_DEFAULT;
+            }
+            return mN.color;
+        }
+
         int resolveNeutralColor() {
             if (mNeutralColor != COLOR_INVALID) {
                 return mNeutralColor;
@@ -5616,13 +5678,14 @@
             return mNeutralColor;
         }
 
-        int resolveAmbientColor() {
-            if (mCachedAmbientColorIsFor == mN.color && mCachedAmbientColorIsFor != COLOR_INVALID) {
+        int resolveAmbientColor(StandardTemplateParams p) {
+            int rawColor = getRawColor(p);
+            if (mCachedAmbientColorIsFor == rawColor && mCachedAmbientColorIsFor != COLOR_INVALID) {
                 return mCachedAmbientColor;
             }
-            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, mN.color);
+            final int contrasted = ContrastColorUtil.resolveAmbientColor(mContext, rawColor);
 
-            mCachedAmbientColorIsFor = mN.color;
+            mCachedAmbientColorIsFor = rawColor;
             return mCachedAmbientColor = contrasted;
         }
 
@@ -5854,9 +5917,9 @@
             return R.layout.notification_material_action_tombstone;
         }
 
-        private int getBackgroundColor() {
-            if (isColorized()) {
-                return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : mN.color;
+        private int getBackgroundColor(StandardTemplateParams p) {
+            if (isColorized(p)) {
+                return mBackgroundColor != COLOR_INVALID ? mBackgroundColor : getRawColor(p);
             } else {
                 return COLOR_DEFAULT;
             }
@@ -5864,10 +5927,11 @@
 
         /**
          * Gets a neutral color that can be used for icons or similar that should not stand out.
+         * @param p the template params to inflate this with
          */
-        private int getNeutralColor() {
-            if (isColorized()) {
-                return getSecondaryTextColor();
+        private int getNeutralColor(StandardTemplateParams p) {
+            if (isColorized(p)) {
+                return getSecondaryTextColor(p);
             } else {
                 return resolveNeutralColor();
             }
@@ -5875,9 +5939,10 @@
 
         /**
          * Same as getBackgroundColor but also resolved the default color to the background.
+         * @param p the template params to inflate this with
          */
-        private int resolveBackgroundColor() {
-            int backgroundColor = getBackgroundColor();
+        private int resolveBackgroundColor(StandardTemplateParams p) {
+            int backgroundColor = getBackgroundColor(p);
             if (backgroundColor == COLOR_DEFAULT) {
                 backgroundColor = mContext.getColor(
                         com.android.internal.R.color.notification_material_background_color);
@@ -5885,10 +5950,6 @@
             return backgroundColor;
         }
 
-        private boolean isColorized() {
-            return mN.isColorized();
-        }
-
         private boolean shouldTintActionButtons() {
             return mTintActionButtons;
         }
@@ -5914,7 +5975,7 @@
             mBackgroundColor = backgroundColor;
             mForegroundColor = foregroundColor;
             mTextColorsAreForBackground = COLOR_INVALID;
-            ensureColors();
+            ensureColors(mParams.reset().fillTextsFrom(this));
         }
 
         /**
@@ -6182,30 +6243,30 @@
         }
 
         protected RemoteViews getStandardView(int layoutId) {
-            return getStandardView(layoutId, null);
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
+            return getStandardView(layoutId, p, null);
         }
 
+
         /**
          * Get the standard view for this style.
          *
-         * @param layoutId The layout id to use
+         * @param layoutId The layout id to use.
+         * @param p the params for this inflation.
          * @param result The result where template bind information is saved.
          * @return A remoteView for this style.
          * @hide
          */
-        protected RemoteViews getStandardView(int layoutId, TemplateBindResult result) {
+        protected RemoteViews getStandardView(int layoutId, StandardTemplateParams p,
+                TemplateBindResult result) {
             checkBuilder();
 
-            // Nasty.
-            CharSequence oldBuilderContentTitle =
-                    mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
             if (mBigContentTitle != null) {
-                mBuilder.setContentTitle(mBigContentTitle);
+                p.title = mBigContentTitle;
             }
 
-            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, result);
-
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
+            RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId, p,
+                    result);
 
             if (mBigContentTitle != null && mBigContentTitle.equals("")) {
                 contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -6500,12 +6561,13 @@
                 mBuilder.mN.largeIcon = null;
             }
 
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder);
             RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource(),
-                    null /* result */);
+                    p, null /* result */);
             if (mSummaryTextSet) {
                 contentView.setTextViewText(R.id.text, mBuilder.processTextSpans(
                         mBuilder.processLegacyText(mSummaryText)));
-                mBuilder.setTextViewColorSecondary(contentView, R.id.text);
+                mBuilder.setTextViewColorSecondary(contentView, R.id.text, p);
                 contentView.setViewVisibility(R.id.text, View.VISIBLE);
             }
             mBuilder.setContentMinHeight(contentView, mBuilder.mN.hasLargeIcon());
@@ -6698,24 +6760,24 @@
          * @hide
          */
         public RemoteViews makeBigContentView() {
-
-            // Nasty
-            CharSequence text = mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
             TemplateBindResult result = new TemplateBindResult();
-            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), result);
+            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource(), p,
+                    result);
             contentView.setInt(R.id.big_text, "setImageEndMargin", result.getIconMarginEnd());
 
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, text);
-
             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
             if (TextUtils.isEmpty(bigTextText)) {
                 // In case the bigtext is null / empty fall back to the normal text to avoid a weird
                 // experience
-                bigTextText = mBuilder.processLegacyText(text);
+                bigTextText = mBuilder.processLegacyText(
+                        mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT));
             }
-            applyBigTextContentView(mBuilder, contentView, bigTextText);
+            contentView.setTextViewText(R.id.big_text, mBuilder.processTextSpans(bigTextText));
+            mBuilder.setTextViewColorSecondary(contentView, R.id.big_text, p);
+            contentView.setViewVisibility(R.id.big_text,
+                    TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
+            contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.hasLargeIcon());
 
             return contentView;
         }
@@ -6733,14 +6795,6 @@
             return !Objects.equals(String.valueOf(getBigText()), String.valueOf(newS.getBigText()));
         }
 
-        static void applyBigTextContentView(Builder builder,
-                RemoteViews contentView, CharSequence bigTextText) {
-            contentView.setTextViewText(R.id.big_text, builder.processTextSpans(bigTextText));
-            builder.setTextViewColorSecondary(contentView, R.id.big_text);
-            contentView.setViewVisibility(R.id.big_text,
-                    TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
-            contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
-        }
     }
 
     /**
@@ -7224,24 +7278,26 @@
                 isOneToOne = !isGroupConversation();
             }
             TemplateBindResult bindResult = new TemplateBindResult();
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).title(
+                    conversationTitle).text(null)
+                    .hideLargeIcon(hideRightIcons || isOneToOne)
+                    .hideReplyIcon(hideRightIcons)
+                    .headerTextSecondary(conversationTitle);
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
-                    mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
-                            .hideLargeIcon(hideRightIcons || isOneToOne)
-                            .hideReplyIcon(hideRightIcons)
-                            .headerTextSecondary(conversationTitle),
+                    p,
                     bindResult);
             addExtras(mBuilder.mN.extras);
             // also update the end margin if there is an image
             contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
                     bindResult.getIconMarginEnd());
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
-                    mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
-                            : mBuilder.resolveContrastColor());
+                    mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+                            : mBuilder.resolveContrastColor(p));
             contentView.setInt(R.id.status_bar_latest_event_content, "setSenderTextColor",
-                    mBuilder.getPrimaryTextColor());
+                    mBuilder.getPrimaryTextColor(p));
             contentView.setInt(R.id.status_bar_latest_event_content, "setMessageTextColor",
-                    mBuilder.getSecondaryTextColor());
+                    mBuilder.getSecondaryTextColor(p));
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
                     displayImagesAtEnd);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setAvatarReplacement",
@@ -7705,15 +7761,9 @@
          * @hide
          */
         public RemoteViews makeBigContentView() {
-            // Remove the content text so it disappears unless you have a summary
-            // Nasty
-            CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
-
+            StandardTemplateParams p = mBuilder.mParams.reset().fillTextsFrom(mBuilder).text(null);
             TemplateBindResult result = new TemplateBindResult();
-            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), result);
-
-            mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
+            RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource(), p, result);
 
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
                     R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
@@ -7760,7 +7810,7 @@
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                     contentView.setTextViewText(rowIds[i],
                             mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
-                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
+                    mBuilder.setTextViewColorSecondary(contentView, rowIds[i], p);
                     contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
                     handleInboxImageMargin(contentView, rowIds[i], first,
                             result.getIconMarginEnd());
@@ -7997,7 +8047,7 @@
         }
 
         private void bindMediaActionButton(RemoteViews container, @IdRes int buttonId,
-                Action action, int color) {
+                Action action, StandardTemplateParams p) {
             final boolean tombstone = (action.actionIntent == null);
             container.setViewVisibility(buttonId, View.VISIBLE);
             container.setImageViewIcon(buttonId, action.getIcon());
@@ -8008,8 +8058,8 @@
             Configuration currentConfig = resources.getConfiguration();
             boolean inNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                     == Configuration.UI_MODE_NIGHT_YES;
-            int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized()
-                    ? color
+            int tintColor = mBuilder.shouldTintActionButtons() || mBuilder.isColorized(p)
+                    ? getActionColor(p)
                     : ContrastColorUtil.resolveColor(mBuilder.mContext,
                             Notification.COLOR_DEFAULT, inNightMode);
 
@@ -8031,8 +8081,10 @@
         }
 
         private RemoteViews makeMediaContentView() {
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+                    mBuilder);
             RemoteViews view = mBuilder.applyStandardTemplate(
-                    R.layout.notification_template_material_media, false, /* hasProgress */
+                    R.layout.notification_template_material_media, p,
                     null /* result */);
 
             final int numActions = mBuilder.mActions.size();
@@ -8047,7 +8099,7 @@
             for (int i = 0; i < MAX_MEDIA_BUTTONS_IN_COMPACT; i++) {
                 if (i < numActionsToShow) {
                     final Action action = mBuilder.mActions.get(mActionsToShowInCompact[i]);
-                    bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, getActionColor());
+                    bindMediaActionButton(view, MEDIA_BUTTON_IDS[i], action, p);
                 } else {
                     view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
@@ -8062,9 +8114,9 @@
             return view;
         }
 
-        private int getActionColor() {
-            return mBuilder.isColorized() ? mBuilder.getPrimaryTextColor()
-                    : mBuilder.resolveContrastColor();
+        private int getActionColor(StandardTemplateParams p) {
+            return mBuilder.isColorized(p) ? mBuilder.getPrimaryTextColor(p)
+                    : mBuilder.resolveContrastColor(p);
         }
 
         private RemoteViews makeMediaBigContentView() {
@@ -8076,13 +8128,14 @@
             if (!mBuilder.mN.hasLargeIcon() && actionCount <= actionsInCompact) {
                 return null;
             }
+            StandardTemplateParams p = mBuilder.mParams.reset().hasProgress(false).fillTextsFrom(
+                    mBuilder);
             RemoteViews big = mBuilder.applyStandardTemplate(
-                    R.layout.notification_template_material_big_media, false, null /* result */);
+                    R.layout.notification_template_material_big_media, p , null /* result */);
 
             for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
                 if (i < actionCount) {
-                    bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i),
-                            getActionColor());
+                    bindMediaActionButton(big, MEDIA_BUTTON_IDS[i], mBuilder.mActions.get(i), p);
                 } else {
                     big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE);
                 }
@@ -8210,7 +8263,7 @@
                 customContent = customContent.clone();
                 remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
                 remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
-                remoteViews.setReapplyDisallowed();
+                remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
             }
             // also update the end margin if there is an image
             Resources resources = mBuilder.mContext.getResources();
@@ -8338,10 +8391,10 @@
                 // Need to clone customContent before adding, because otherwise it can no longer be
                 // parceled independently of remoteViews.
                 customContent = customContent.clone();
-                customContent.overrideTextColors(mBuilder.getPrimaryTextColor());
+                customContent.overrideTextColors(mBuilder.getPrimaryTextColor(mBuilder.mParams));
                 remoteViews.removeAllViews(id);
                 remoteViews.addView(id, customContent);
-                remoteViews.setReapplyDisallowed();
+                remoteViews.addFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED);
             }
             return remoteViews;
         }
@@ -9881,17 +9934,23 @@
         CharSequence title;
         CharSequence text;
         CharSequence headerTextSecondary;
+        CharSequence summaryText;
         int maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
         boolean hideLargeIcon;
         boolean hideReplyIcon;
+        boolean allowColorization  = true;
+        boolean forceDefaultColor = false;
 
         final StandardTemplateParams reset() {
             hasProgress = true;
             ambient = false;
             title = null;
             text = null;
+            summaryText = null;
             headerTextSecondary = null;
             maxRemoteInputHistory = Style.MAX_REMOTE_INPUT_HISTORY_LINES;
+            allowColorization = true;
+            forceDefaultColor = false;
             return this;
         }
 
@@ -9910,6 +9969,11 @@
             return this;
         }
 
+        final StandardTemplateParams summaryText(CharSequence text) {
+            this.summaryText = text;
+            return this;
+        }
+
         final StandardTemplateParams headerTextSecondary(CharSequence text) {
             this.headerTextSecondary = text;
             return this;
@@ -9925,6 +9989,16 @@
             return this;
         }
 
+        final StandardTemplateParams disallowColorization() {
+            this.allowColorization = false;
+            return this;
+        }
+
+        final StandardTemplateParams forceDefaultColor() {
+            this.forceDefaultColor = true;
+            return this;
+        }
+
         final StandardTemplateParams ambient(boolean ambient) {
             Preconditions.checkState(title == null && text == null, "must set ambient before text");
             this.ambient = ambient;
@@ -9941,6 +10015,7 @@
                 text = extras.getCharSequence(EXTRA_TEXT);
             }
             this.text = b.processLegacyText(text, ambient);
+            this.summaryText = extras.getCharSequence(EXTRA_SUB_TEXT);
             return this;
         }
 
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 89ec19b..25fa897 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1424,7 +1424,62 @@
             return other.priorityCategories == priorityCategories
                     && other.priorityCallSenders == priorityCallSenders
                     && other.priorityMessageSenders == priorityMessageSenders
-                    && other.suppressedVisualEffects == suppressedVisualEffects;
+                    && suppressedVisualEffectsEqual(suppressedVisualEffects,
+                    other.suppressedVisualEffects);
+        }
+
+
+        private boolean suppressedVisualEffectsEqual(int suppressedEffects,
+                int otherSuppressedVisualEffects) {
+            if (suppressedEffects == otherSuppressedVisualEffects) {
+                return true;
+            }
+
+            if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+                suppressedEffects |= SUPPRESSED_EFFECT_PEEK;
+            }
+            if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+                suppressedEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+                suppressedEffects |= SUPPRESSED_EFFECT_LIGHTS;
+                suppressedEffects |= SUPPRESSED_EFFECT_AMBIENT;
+            }
+
+            if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0) {
+                otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
+            }
+            if ((otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0) {
+                otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+                otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS;
+                otherSuppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
+            }
+
+            if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON)
+                    != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_ON)) {
+                int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0
+                        ? otherSuppressedVisualEffects : suppressedEffects;
+                if ((currSuppressedEffects & SUPPRESSED_EFFECT_PEEK) == 0) {
+                    return false;
+                }
+            }
+
+            if ((suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF)
+                    != (otherSuppressedVisualEffects & SUPPRESSED_EFFECT_SCREEN_OFF)) {
+                int currSuppressedEffects = (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0
+                        ? otherSuppressedVisualEffects : suppressedEffects;
+                if ((currSuppressedEffects & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0
+                        || (currSuppressedEffects & SUPPRESSED_EFFECT_LIGHTS) == 0
+                        || (currSuppressedEffects & SUPPRESSED_EFFECT_AMBIENT) == 0) {
+                    return false;
+                }
+            }
+
+            int thisWithoutOldEffects = suppressedEffects
+                    & ~SUPPRESSED_EFFECT_SCREEN_ON
+                    & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+            int otherWithoutOldEffects = otherSuppressedVisualEffects
+                    & ~SUPPRESSED_EFFECT_SCREEN_ON
+                    & ~SUPPRESSED_EFFECT_SCREEN_OFF;
+            return thisWithoutOldEffects == otherWithoutOldEffects;
         }
 
         @Override
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index 9df4fff..95d5d19 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -32,13 +32,11 @@
     public final long rssInBytes;
     public final long cacheInBytes;
     public final long swapInBytes;
-    // TODO(rslawik): Delete this field once ProcessMemoryHighWaterMark is ready.
-    public final long rssHighWatermarkInBytes;
     public final long startTimeNanos;
 
     public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
                               long pgmajfault, long rssInBytes, long cacheInBytes,
-                              long swapInBytes, long rssHighWatermarkInBytes, long startTimeNanos) {
+                              long swapInBytes, long startTimeNanos) {
         this.uid = uid;
         this.processName = processName;
         this.oomScore = oomScore;
@@ -47,7 +45,6 @@
         this.rssInBytes = rssInBytes;
         this.cacheInBytes = cacheInBytes;
         this.swapInBytes = swapInBytes;
-        this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
         this.startTimeNanos = startTimeNanos;
     }
 
@@ -60,7 +57,6 @@
         rssInBytes = in.readLong();
         cacheInBytes = in.readLong();
         swapInBytes = in.readLong();
-        rssHighWatermarkInBytes = in.readLong();
         startTimeNanos = in.readLong();
     }
 
@@ -91,7 +87,6 @@
         parcel.writeLong(rssInBytes);
         parcel.writeLong(cacheInBytes);
         parcel.writeLong(swapInBytes);
-        parcel.writeLong(rssHighWatermarkInBytes);
         parcel.writeLong(startTimeNanos);
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0123551..dfe371c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -165,8 +165,8 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.IAutoFillManager;
 import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.ContentCaptureManager;
 import android.view.intelligence.IIntelligenceManager;
-import android.view.intelligence.IntelligenceManager;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textservice.TextServicesManager;
 
@@ -199,6 +199,7 @@
     private SystemServiceRegistry() { }
 
     static {
+        //CHECKSTYLE:OFF IndentationCheck
         registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
                 new CachedServiceFetcher<AccessibilityManager>() {
             @Override
@@ -1050,15 +1051,20 @@
                 return new AutofillManager(ctx.getOuterContext(), service);
             }});
 
-        registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class,
-                new CachedServiceFetcher<IntelligenceManager>() {
+        registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class,
+                new CachedServiceFetcher<ContentCaptureManager>() {
             @Override
-            public IntelligenceManager createService(ContextImpl ctx)
+            public ContentCaptureManager createService(ContextImpl ctx)
                     throws ServiceNotFoundException {
                 // Get the services without throwing as this is an optional feature
-                IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE);
-                IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
-                return new IntelligenceManager(ctx.getOuterContext(), service);
+                Context outerContext = ctx.getOuterContext();
+                if (outerContext.isContentCaptureSupported()) {
+                    IBinder b = ServiceManager
+                            .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+                    IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
+                    return new ContentCaptureManager(outerContext, service);
+                }
+                return null;
             }});
 
         registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@@ -1138,6 +1144,7 @@
                             throws ServiceNotFoundException {
                         return new RoleManager(ctx.getOuterContext());
                     }});
+        //CHECKSTYLE:ON IndentationCheck
     }
 
     /**
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 3ea3da2..f0f7d89 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.SystemApi;
 import android.app.slice.Slice;
 import android.content.ComponentName;
 import android.content.Context;
@@ -330,7 +331,9 @@
      * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
      * @see WallpaperService.Engine#isInAmbientMode()
      * @return {@code true} if wallpaper can draw when in ambient mode.
+     * @hide
      */
+    @SystemApi
     public boolean supportsAmbientMode() {
         return mSupportsAmbientMode;
     }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27471ca..27ae0b0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1898,23 +1898,33 @@
      * @hide
      */
     public static ComponentName getDefaultWallpaperComponent(Context context) {
+        ComponentName cn = null;
+
         String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
         if (!TextUtils.isEmpty(flat)) {
-            final ComponentName cn = ComponentName.unflattenFromString(flat);
-            if (cn != null) {
-                return cn;
+            cn = ComponentName.unflattenFromString(flat);
+        }
+
+        if (cn == null) {
+            flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+            if (!TextUtils.isEmpty(flat)) {
+                cn = ComponentName.unflattenFromString(flat);
             }
         }
 
-        flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
-        if (!TextUtils.isEmpty(flat)) {
-            final ComponentName cn = ComponentName.unflattenFromString(flat);
-            if (cn != null) {
-                return cn;
+        // Check if the package exists
+        if (cn != null) {
+            try {
+                final PackageManager packageManager = context.getPackageManager();
+                packageManager.getPackageInfo(cn.getPackageName(),
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+            } catch (PackageManager.NameNotFoundException e) {
+                cn = null;
             }
         }
 
-        return null;
+        return cn;
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 00c1863..98d2a40 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9992,4 +9992,111 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Whitelists a package that is allowed to access cross profile calendar APIs.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName name of the package to be whitelisted.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     */
+    public void addCrossProfileCalendarPackage(@NonNull ComponentName admin,
+            @NonNull String packageName) {
+        throwIfParentInstance("addCrossProfileCalendarPackage");
+        if (mService != null) {
+            try {
+                mService.addCrossProfileCalendarPackage(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Removes a package that was allowed to access cross profile calendar APIs
+     * from the whitelist.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName name of the package to be removed from the whitelist.
+     * @return {@code true} if the package is successfully removed from the whitelist,
+     * {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     */
+    public boolean removeCrossProfileCalendarPackage(@NonNull ComponentName admin,
+            @NonNull String packageName) {
+        throwIfParentInstance("removeCrossProfileCalendarPackage");
+        if (mService != null) {
+            try {
+                return mService.removeCrossProfileCalendarPackage(admin, packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gets a set of package names that are whitelisted to access cross profile calendar APIs.
+     *
+     * <p>Called by a profile owner of a managed profile.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return the set of names of packages that were previously whitelisted via
+     * {@link #addCrossProfileCalendarPackage(ComponentName, String)}, or an
+     * empty set if none have been whitelisted.
+     * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     */
+    public @NonNull Set<String> getCrossProfileCalendarPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileCalendarPackages");
+        if (mService != null) {
+            try {
+                return new ArraySet<>(mService.getCrossProfileCalendarPackages(admin));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    /**
+     * Returns if a package is whitelisted to access cross profile calendar APIs.
+     *
+     * <p>To query for a specific user, use
+     * {@link Context#createPackageContextAsUser(String, int, UserHandle)} to create a context for
+     * that user, and get a {@link DevicePolicyManager} from this context.
+     *
+     * @param packageName the name of the package
+     * @return {@code true} if the package is whitelisted to access cross profile calendar APIs.
+     * {@code false} otherwise.
+     *
+     * @see #addCrossProfileCalendarPackage(ComponentName, String)
+     * @see #removeCrossProfileCalendarPackage(ComponentName, String)
+     * @see #getCrossProfileCalendarPackages(ComponentName)
+     * @hide
+     */
+    public @NonNull boolean isPackageAllowedToAccessCalendar(@NonNull  String packageName) {
+        throwIfParentInstance("isPackageAllowedToAccessCalendar");
+        if (mService != null) {
+            try {
+                return mService.isPackageAllowedToAccessCalendarForUser(packageName,
+                        mContext.getUserId());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 60f79d6..297676d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -422,4 +422,9 @@
     void grantDeviceIdsAccessToProfileOwner(in ComponentName who, int userId);
 
     void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener);
+
+    void addCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+    boolean removeCrossProfileCalendarPackage(in ComponentName admin, String packageName);
+    List<String> getCrossProfileCalendarPackages(in ComponentName admin);
+    boolean isPackageAllowedToAccessCalendarForUser(String packageName, int userHandle);
 }
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
index 1c9a43a..673d85f 100644
--- a/core/java/android/app/backup/OWNERS
+++ b/core/java/android/app/backup/OWNERS
@@ -1,7 +1,6 @@
-artikz@google.com
+anniemeng@google.com
 brufino@google.com
 bryanmawhinney@google.com
 ctate@google.com
 jorlow@google.com
-mkarpinski@google.com
 
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 318dbee..c740c42 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -88,6 +88,7 @@
     int mViewMode = VIEW_MODE_NOINIT;
     int mLayoutId = -1;
     private OnClickHandler mOnClickHandler;
+    private boolean mOnLightBackground;
 
     private Executor mAsyncExecutor;
     private CancellationSignal mLastExecutionSignal;
@@ -374,6 +375,15 @@
     }
 
     /**
+     * Sets whether the widget should is being displayed on a light/white background and use an
+     * alternate UI if available.
+     * @see RemoteViews#setLightBackgroundLayoutId(int)
+     */
+    public void setOnLightBackground(boolean useDarkTextLayout) {
+        mOnLightBackground = useDarkTextLayout;
+    }
+
+    /**
      * Update the AppWidgetProviderInfo for this view, and reset it to the
      * initial layout.
      */
@@ -413,6 +423,10 @@
             mLayoutId = -1;
             mViewMode = VIEW_MODE_DEFAULT;
         } else {
+            if (mOnLightBackground) {
+                remoteViews = remoteViews.getDarkTextViews();
+            }
+
             if (mAsyncExecutor != null && useAsyncIfPossible) {
                 inflateAsync(remoteViews);
                 return;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 237082e..2f0b44f 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.EventLog;
 
 
 /**
@@ -30,6 +31,8 @@
  */
 public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
 
+    private static final int MAX_DESCRIPTOR_SIZE = 2048;
+
     private final String mName;
     private final String mDescription;
     private final String mProvider;
@@ -55,6 +58,12 @@
         mDescription = description;
         mProvider = provider;
         mSubclass = subclass;
+
+        if (descriptors == null || descriptors.length > MAX_DESCRIPTOR_SIZE) {
+            EventLog.writeEvent(0x534e4554, "119819889", -1, "");
+            throw new IllegalArgumentException("descriptors must be not null and shorter than "
+                    + MAX_DESCRIPTOR_SIZE);
+        }
         mDescriptors = descriptors.clone();
     }
 
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 11f8ab7..e3672a7 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -52,8 +52,7 @@
 @RequiresFeature(PackageManager.FEATURE_BLUETOOTH)
 public final class BluetoothManager {
     private static final String TAG = "BluetoothManager";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = true;
+    private static final boolean DBG = false;
 
     private final BluetoothAdapter mAdapter;
 
diff --git a/core/java/android/bluetooth/le/ScanRecord.java b/core/java/android/bluetooth/le/ScanRecord.java
index 7988008..2174255 100644
--- a/core/java/android/bluetooth/le/ScanRecord.java
+++ b/core/java/android/bluetooth/le/ScanRecord.java
@@ -116,6 +116,9 @@
      */
     @Nullable
     public byte[] getManufacturerSpecificData(int manufacturerId) {
+        if (mManufacturerSpecificData == null) {
+            return null;
+        }
         return mManufacturerSpecificData.get(manufacturerId);
     }
 
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 437039d..7d5202d 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -97,8 +97,7 @@
      *
      * @hide
      */
-    public static final boolean DEPRECATE_DATA_COLUMNS = SystemProperties
-            .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+    public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
 
     /**
      * Special filesystem path prefix which indicates that a path should be
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cec8ef5..68aac64 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -741,16 +741,22 @@
     /** Return the name of this application's package. */
     public abstract String getPackageName();
 
-    /** @hide Return the name of the base context this context is derived from. */
+    /**
+     * @hide Return the name of the base context this context is derived from.
+     * This is the same as {@link #getOpPackageName()} except in
+     * cases where system components are loaded into other app processes, in which
+     * case {@link #getOpPackageName()} will be the name of the primary package in
+     * that process (so that app ops uid verification will work with the name).
+     */
     @UnsupportedAppUsage
     public abstract String getBasePackageName();
 
-    /** @hide Return the package name that should be used for app ops calls from
-     * this context.  This is the same as {@link #getBasePackageName()} except in
-     * cases where system components are loaded into other app processes, in which
-     * case this will be the name of the primary package in that process (so that app
-     * ops uid verification will work with the name). */
-    @TestApi
+    /**
+     * Return the package name that should be used for {@link android.app.AppOpsManager} calls from
+     * this context, so that app ops manager's uid verification will work with the name.
+     * <p>
+     * This is not generally intended for third party application developers.
+     */
     public abstract String getOpPackageName();
 
     /** Return the full application info for this context's package. */
@@ -3927,12 +3933,13 @@
     public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
 
     /**
-     * Official published name of the intelligence service.
+     * Official published name of the smart suggestions service.
      *
      * @hide
      * @see #getSystemService(String)
      */
-    public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence";
+    // TODO(b/111276913): rename string (will require SELinux change first)
+    public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "intelligence";
 
     /**
      * Use with {@link #getSystemService(String)} to access the
@@ -5217,6 +5224,25 @@
     }
 
     /**
+     * Checks whether this context supports content capture.
+     *
+     * @hide
+     */
+    // NOTE: for now we just need to check if it's supported so we can optimize calls that can be
+    // skipped when it isn't. Eventually, we might need a full
+    // ContentCaptureManager.ContentCaptureClient interface (as it's done with AutofillClient).
+    //
+    public boolean isContentCaptureSupported() {
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public void setContentCaptureSupported(@SuppressWarnings("unused") boolean supported) {
+    }
+
+    /**
      * Throws an exception if the Context is using system resources,
      * which are non-runtime-overlay-themable and may show inconsistent UI.
      * @hide
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 2db44b4..26ed3b7 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1049,4 +1049,20 @@
             mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled);
         }
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isContentCaptureSupported() {
+        return mBase.isContentCaptureSupported();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void setContentCaptureSupported(boolean supported) {
+        mBase.setContentCaptureSupported(supported);
+    }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6bfddb0..edfb3a7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1757,33 +1757,6 @@
             "android.intent.action.MANAGE_APP_PERMISSIONS";
 
     /**
-     * Activity action: Launch UI to manage a specific permissions of an app.
-     * <p>
-     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permission
-     * will be managed by the launched UI.
-     * </p>
-     * <p>
-     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the (individual) permission
-     * that should be managed by the launched UI.
-     * </p>
-     * <p>
-     * <li> {@link #EXTRA_USER} specifies the UserHandle of the user that owns the app.
-     * </p>
-     * <p>
-     * Output: Nothing.
-     * </p>
-     *
-     * @see #EXTRA_PACKAGE_NAME
-     * @see #EXTRA_PERMISSION_NAME
-     *
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_MANAGE_APP_PERMISSION =
-            "android.intent.action.MANAGE_APP_PERMISSION";
-
-    /**
      * Activity action: Launch UI to manage permissions.
      * <p>
      * Input: Nothing.
@@ -3601,6 +3574,16 @@
     public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE";
 
     /**
+     * Activity Action: Define the meaning of the selected word(s).
+     * <p>
+     * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to define.
+     * <p>
+     * Output: nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DEFINE = "android.intent.action.DEFINE";
+
+    /**
      * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or
      * exisiting sensor being disconnected.
      *
@@ -4106,6 +4089,18 @@
      */
     public static final String ACTION_DOCK_ACTIVE = "android.intent.action.DOCK_ACTIVE";
 
+    /**
+     * Broadcast Action: Indicates that a new device customization has been
+     * downloaded and applied (packages installed, runtime resource overlays
+     * enabled, xml files copied, ...), and that it is time for components that
+     * need to for example clear their caches to do so now.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_DEVICE_CUSTOMIZATION_READY =
+            "android.intent.action.DEVICE_CUSTOMIZATION_READY";
+
 
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4e110da..98a135f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -32,7 +32,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.text.TextUtils;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -464,16 +463,6 @@
     public static final int PRIVATE_FLAG_CANT_SAVE_STATE = 1<<1;
 
     /**
-     * Value for {@link #privateFlags}: Set to true if the application has been
-     * installed using the forward lock option.
-     *
-     * NOTE: DO NOT CHANGE THIS VALUE!  It is saved in packages.xml.
-     *
-     * {@hide}
-     */
-    public static final int PRIVATE_FLAG_FORWARD_LOCK = 1<<2;
-
-    /**
      * Value for {@link #privateFlags}: set to {@code true} if the application
      * is permitted to hold privileged permissions.
      *
@@ -651,7 +640,6 @@
             PRIVATE_FLAG_CANT_SAVE_STATE,
             PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
             PRIVATE_FLAG_DIRECT_BOOT_AWARE,
-            PRIVATE_FLAG_FORWARD_LOCK,
             PRIVATE_FLAG_HAS_DOMAIN_URLS,
             PRIVATE_FLAG_HIDDEN,
             PRIVATE_FLAG_INSTANT,
@@ -1782,7 +1770,7 @@
      * is on the package whitelist.
      *
      * @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
-     *               if nothing configured.
+     *        if nothing configured.
      * @hide
      */
     public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
@@ -1843,17 +1831,6 @@
         return (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
-    /** @hide */
-    public boolean isExternalAsec() {
-        return TextUtils.isEmpty(volumeUuid) && isExternal();
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean isForwardLocked() {
-        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     /**
      * True if the application is installed as an instant app.
      * @hide
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d0eff2e..dbea821 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -676,4 +676,6 @@
     String getSystemTextClassifierPackageName();
 
     boolean isPackageStateProtected(String packageName, int userId);
+
+    void sendDeviceCustomizationReadyBroadcast();
 }
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ecdd810..099d15a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -22,6 +22,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Overall information about the contents of a package.  This corresponds
  * to all of the information collected from AndroidManifest.xml.
@@ -204,7 +207,10 @@
      * {@link PackageManager#GET_PERMISSIONS} was set.  This list includes
      * all permissions requested, even those that were not granted or known
      * by the system at install time.
+     *
+     * @deprecated Use {@link #usesPermissions}
      */
+    @Deprecated
     public String[] requestedPermissions;
 
     /**
@@ -214,10 +220,23 @@
      * {@link PackageManager#GET_PERMISSIONS} was set.  Each value matches
      * the corresponding entry in {@link #requestedPermissions}, and will have
      * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+     *
+     * @deprecated Use {@link #usesPermissions}
      */
+    @Deprecated
     public int[] requestedPermissionsFlags;
 
     /**
+     * Array of all {@link android.R.styleable#AndroidManifestUsesPermission
+     * &lt;uses-permission&gt;} tags included under &lt;manifest&gt;,
+     * or null if there were none.  This is only filled in if the flag
+     * {@link PackageManager#GET_PERMISSIONS} was set.  This list includes
+     * all permissions requested, even those that were not granted or known
+     * by the system at install time.
+     */
+    public UsesPermissionInfo[] usesPermissions;
+
+    /**
      * Flag for {@link #requestedPermissionsFlags}: the requested permission
      * is required for the application to run; the user can not optionally
      * disable it.  Currently all permissions are required.
@@ -456,6 +475,7 @@
         dest.writeTypedArray(permissions, parcelableFlags);
         dest.writeStringArray(requestedPermissions);
         dest.writeIntArray(requestedPermissionsFlags);
+        dest.writeTypedArray(usesPermissions, parcelableFlags);
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
@@ -520,6 +540,7 @@
         permissions = source.createTypedArray(PermissionInfo.CREATOR);
         requestedPermissions = source.createStringArray();
         requestedPermissionsFlags = source.createIntArray();
+        usesPermissions = source.createTypedArray(UsesPermissionInfo.CREATOR);
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 8f90199..07672d9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1374,12 +1374,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsInternal() {
-            installFlags |= PackageManager.INSTALL_INTERNAL;
-            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
-        }
-
-        /** {@hide} */
         @SystemApi
         public void setAllowDowngrade(boolean allowDowngrade) {
             if (allowDowngrade) {
@@ -1390,12 +1384,6 @@
         }
 
         /** {@hide} */
-        public void setInstallFlagsExternal() {
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-            installFlags &= ~PackageManager.INSTALL_INTERNAL;
-        }
-
-        /** {@hide} */
         public void setInstallFlagsForcePermissionPrompt() {
             installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 361beba..b7df2bf 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -700,10 +700,8 @@
 
     /** @hide */
     @IntDef(flag = true, prefix = { "INSTALL_" }, value = {
-            INSTALL_FORWARD_LOCK,
             INSTALL_REPLACE_EXISTING,
             INSTALL_ALLOW_TEST,
-            INSTALL_EXTERNAL,
             INSTALL_INTERNAL,
             INSTALL_FROM_ADB,
             INSTALL_ALL_USERS,
@@ -721,17 +719,6 @@
     public @interface InstallFlags {}
 
     /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
-     * should be installed as forward locked, i.e. only the app itself should
-     * have access to its code and non-resource assets.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_FORWARD_LOCK = 0x00000001;
-
-    /**
      * Flag parameter for {@link #installPackage} to indicate that you want to
      * replace an already installed package, if one exists.
      *
@@ -750,17 +737,6 @@
 
     /**
      * Flag parameter for {@link #installPackage} to indicate that this package
-     * must be installed to an ASEC on a {@link VolumeInfo#TYPE_PUBLIC}.
-     *
-     * @deprecated new installs into ASEC containers are no longer supported;
-     *             use adoptable storage instead.
-     * @hide
-     */
-    @Deprecated
-    public static final int INSTALL_EXTERNAL = 0x00000008;
-
-    /**
-     * Flag parameter for {@link #installPackage} to indicate that this package
      * must be installed to internal storage.
      *
      * @hide
@@ -1521,14 +1497,6 @@
 
     /**
      * Error code that is passed to the {@link IPackageMoveObserver} if the
-     * specified package cannot be moved since its forward locked.
-     *
-     * @hide
-     */
-    public static final int MOVE_FAILED_FORWARD_LOCKED = -4;
-
-    /**
-     * Error code that is passed to the {@link IPackageMoveObserver} if the
      * specified package cannot be moved to the specified location.
      *
      * @hide
@@ -5404,6 +5372,10 @@
     public abstract void removePackageFromPreferred(String packageName);
 
     /**
+     * @deprecated This function no longer does anything; it was an old
+     * approach to managing preferred activities, which has been superseded
+     * by (and conflicts with) the modern activity-based preferences.
+     *
      * Retrieve the list of all currently configured preferred packages. The
      * first package on the list is the most preferred, the last is the least
      * preferred.
@@ -5412,6 +5384,7 @@
      * @return A List of PackageInfo objects, one for each preferred
      *         application, in order of preference.
      */
+    @Deprecated
     public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
 
     /**
@@ -5438,11 +5411,16 @@
             ComponentName[] set, ComponentName activity);
 
     /**
+     * @deprecated This is a protected API that should not have been available
+     * to third party applications.  It is the platform's responsibility for
+     * assigning preferred activities and this cannot be directly modified.
+     *
      * Same as {@link #addPreferredActivity(IntentFilter, int,
             ComponentName[], ComponentName)}, but with a specific userId to apply the preference
             to.
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public void addPreferredActivityAsUser(IntentFilter filter, int match,
             ComponentName[] set, ComponentName activity, @UserIdInt int userId) {
@@ -5476,6 +5454,10 @@
             ComponentName[] set, ComponentName activity);
 
     /**
+     * @deprecated This is a protected API that should not have been available
+     * to third party applications.  It is the platform's responsibility for
+     * assigning preferred activities and this cannot be directly modified.
+     *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used to automatically select the given activity
      * component when {@link Context#startActivity(Intent) Context.startActivity()} finds multiple
@@ -5491,6 +5473,7 @@
      *
      * @hide
      */
+    @Deprecated
     @SystemApi
     public void replacePreferredActivity(@NonNull IntentFilter filter, int match,
             @NonNull List<ComponentName> set, @NonNull ComponentName activity) {
@@ -5508,6 +5491,10 @@
     }
 
     /**
+     * @deprecated This function no longer does anything; it was an old
+     * approach to managing preferred activities, which has been superseded
+     * by (and conflicts with) the modern activity-based preferences.
+     *
      * Remove all preferred activity mappings, previously added with
      * {@link #addPreferredActivity}, from the
      * system whose activities are implemented in the given package name.
@@ -5516,9 +5503,14 @@
      * @param packageName The name of the package whose preferred activity
      * mappings are to be removed.
      */
+    @Deprecated
     public abstract void clearPackagePreferredActivities(String packageName);
 
     /**
+     * @deprecated This function no longer does anything; it was an old
+     * approach to managing preferred activities, which has been superseded
+     * by (and conflicts with) the modern activity-based preferences.
+     *
      * Retrieve all preferred activities, previously added with
      * {@link #addPreferredActivity}, that are
      * currently registered with the system.
@@ -5535,6 +5527,7 @@
      * (the number of distinct IntentFilter records, not the number of unique
      * activity components) that were found.
      */
+    @Deprecated
     public abstract int getPreferredActivities(@NonNull List<IntentFilter> outFilters,
             @NonNull List<ComponentName> outActivities, String packageName);
 
@@ -6441,4 +6434,18 @@
             "isPackageStateProtected not implemented in subclass");
     }
 
+    /**
+     * Notify to the rest of the system that a new device configuration has
+     * been prepared and that it is time to refresh caches.
+     *
+     * @see android.content.Intent#ACTION_DEVICE_CUSTOMIZATION_READY
+     *
+     * @hide
+     */
+    @SystemApi
+    public void sendDeviceCustomizationReadyBroadcast() {
+        throw new UnsupportedOperationException(
+            "sendDeviceCustomizationReadyBroadcast not implemented in subclass");
+    }
+
 }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 6f49cc4..b49c447 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager.ResolveInfoFlags;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.util.ArraySet;
 import android.util.SparseArray;
 
 import com.android.internal.util.function.TriFunction;
@@ -37,6 +38,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.function.BiFunction;
+import java.util.function.Consumer;
 
 /**
  * Package manager local system service interface.
@@ -735,4 +737,22 @@
 
     /** Returns {@code true} if the given user requires extra badging for icons. */
     public abstract boolean userNeedsBadging(int userId);
+
+    /**
+     * Perform the given action for each package.
+     * Note that packages lock will be held while performin the actions.
+     *
+     * @param actionLocked action to be performed
+     */
+    public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked);
+
+    /** Returns the list of enabled components */
+    public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
+
+    /** Returns the list of disabled components */
+    public abstract ArraySet<String> getDisabledComponents(String packageName, int userId);
+
+    /** Returns whether the given package is enabled for the given user */
+    public abstract @PackageManager.EnabledState int getApplicationEnabledState(
+            String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2c2776b..ac18dca 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -785,18 +785,23 @@
                     pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
                 }
             }
-            N = p.requestedPermissions.size();
+            N = p.usesPermissionInfos.size();
             if (N > 0) {
                 pi.requestedPermissions = new String[N];
                 pi.requestedPermissionsFlags = new int[N];
+                pi.usesPermissions = new UsesPermissionInfo[N];
                 for (int i=0; i<N; i++) {
-                    final String perm = p.requestedPermissions.get(i);
+                    UsesPermissionInfo info = p.usesPermissionInfos.get(i);
+                    final String perm = info.getPermission();
                     pi.requestedPermissions[i] = perm;
+                    int permissionFlags = 0;
                     // The notion of required permissions is deprecated but for compatibility.
-                    pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
+                    permissionFlags |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
                     if (grantedPermissions != null && grantedPermissions.contains(perm)) {
-                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
+                        permissionFlags |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
                     }
+                    pi.requestedPermissionsFlags[i] = permissionFlags;
+                    pi.usesPermissions[i] = new UsesPermissionInfo(info, permissionFlags);
                 }
             }
         }
@@ -829,9 +834,6 @@
 
     public static final int PARSE_MUST_BE_APK = 1 << 0;
     public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
-    /** @deprecated forward lock no longer functional. remove. */
-    @Deprecated
-    public static final int PARSE_FORWARD_LOCK = 1 << 2;
     public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
     public static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
     public static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
@@ -845,7 +847,6 @@
             PARSE_ENFORCE_CODE,
             PARSE_EXTERNAL_STORAGE,
             PARSE_FORCE_SDK,
-            PARSE_FORWARD_LOCK,
             PARSE_IGNORE_PROCESSES,
             PARSE_IS_SYSTEM_DIR,
             PARSE_MUST_BE_APK,
@@ -2006,11 +2007,6 @@
                 PARSE_DEFAULT_TARGET_SANDBOX);
         pkg.applicationInfo.targetSandboxVersion = targetSandboxVersion;
 
-        /* Set the global "forward lock" flag */
-        if ((flags & PARSE_FORWARD_LOCK) != 0) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-        }
-
         /* Set the global "on SD card" flag */
         if ((flags & PARSE_EXTERNAL_STORAGE) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
@@ -2123,12 +2119,12 @@
                     return null;
                 }
             } else if (tagName.equals(TAG_USES_PERMISSION)) {
-                if (!parseUsesPermission(pkg, res, parser)) {
+                if (!parseUsesPermission(pkg, res, parser, outError)) {
                     return null;
                 }
             } else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
                     || tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
-                if (!parseUsesPermission(pkg, res, parser)) {
+                if (!parseUsesPermission(pkg, res, parser, outError)) {
                     return null;
                 }
             } else if (tagName.equals(TAG_USES_CONFIGURATION)) {
@@ -2451,7 +2447,7 @@
                     newPermsMsg.append(' ');
                 }
                 newPermsMsg.append(npi.name);
-                pkg.requestedPermissions.add(npi.name);
+                addRequestedPermission(pkg, npi.name);
                 pkg.implicitPermissions.add(npi.name);
             }
         }
@@ -2472,7 +2468,7 @@
             for (int in = 0; in < newPerms.size(); in++) {
                 final String perm = newPerms.get(in);
                 if (!pkg.requestedPermissions.contains(perm)) {
-                    pkg.requestedPermissions.add(perm);
+                    addRequestedPermission(pkg, perm);
                     pkg.implicitPermissions.add(perm);
                 }
             }
@@ -2517,7 +2513,7 @@
         // If the storage model feature flag is disabled, we need to fiddle
         // around with permission definitions to return us to pre-Q behavior.
         // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (!StorageManager.hasIsolatedStorage()) {
             if ("android".equals(pkg.packageName)) {
                 final ArraySet<String> newGroups = new ArraySet<>();
                 newGroups.add(android.Manifest.permission_group.MEDIA_AURAL);
@@ -2552,13 +2548,13 @@
             }
         } else {
             if (FORCE_AUDIO_PACKAGES.contains(pkg.packageName)) {
-                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_AUDIO);
+                addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_AUDIO);
             }
             if (FORCE_VIDEO_PACKAGES.contains(pkg.packageName)) {
-                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_VIDEO);
+                addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_VIDEO);
             }
             if (FORCE_IMAGES_PACKAGES.contains(pkg.packageName)) {
-                pkg.requestedPermissions.add(android.Manifest.permission.READ_MEDIA_IMAGES);
+                addRequestedPermission(pkg, android.Manifest.permission.READ_MEDIA_IMAGES);
             }
         }
 
@@ -2598,6 +2594,14 @@
     }
 
     /**
+     * Helper method for adding a requested permission to a package outside of a uses-permission.
+     */
+    private void addRequestedPermission(Package pkg, String permission) {
+        pkg.requestedPermissions.add(permission);
+        pkg.usesPermissionInfos.add(new UsesPermissionInfo(permission));
+    }
+
+    /**
      * Computes the targetSdkVersion to use at runtime. If the package is not
      * compatible with this platform, populates {@code outError[0]} with an
      * error message.
@@ -2854,8 +2858,8 @@
         return certSha256Digests;
     }
 
-    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
+    private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
+            String[] outError) throws XmlPullParserException, IOException {
         TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifestUsesPermission);
 
@@ -2879,6 +2883,44 @@
         final String requiredNotfeature = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredNotFeature, 0);
 
+        int dataSentOffDevice = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSentOffDevice, 0);
+
+        int dataSharedWithThirdParty = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_dataSharedWithThirdParty, 0);
+
+        int dataUsedForMonetization = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_dataUsedForMonetization, 0);
+
+        int retentionWeeks = -1;
+        int retention;
+
+        String rawRetention = sa.getString(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime);
+
+        if (rawRetention == null) {
+            retention = UsesPermissionInfo.RETENTION_UNDEFINED;
+        } else if ("notRetained".equals(rawRetention)) {
+            retention = UsesPermissionInfo.RETENTION_NOT_RETAINED;
+        } else if ("userSelected".equals(rawRetention)) {
+            retention = UsesPermissionInfo.RETENTION_USER_SELECTED;
+        } else if ("unlimited".equals(rawRetention)) {
+            retention = UsesPermissionInfo.RETENTION_UNLIMITED;
+        } else {
+            // A number of weeks was specified
+            retention = UsesPermissionInfo.RETENTION_SPECIFIED;
+            retentionWeeks = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestUsesPermission_dataRetentionTime,
+                -1);
+
+            if (retentionWeeks < 0) {
+                outError[0] = "Bad value provided for dataRetentionTime.";
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                XmlUtils.skipCurrentTag(parser);
+                sa.recycle();
+                return false;
+            }
+        }
         sa.recycle();
 
         XmlUtils.skipCurrentTag(parser);
@@ -2911,6 +2953,10 @@
                     + parser.getPositionDescription());
         }
 
+        UsesPermissionInfo info = new UsesPermissionInfo(name, dataSentOffDevice,
+                dataSharedWithThirdParty, dataUsedForMonetization, retention, retentionWeeks);
+        pkg.usesPermissionInfos.add(info);
+
         return true;
     }
 
@@ -3245,6 +3291,10 @@
         perm.info.flags = sa.getInt(
                 com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
 
+        perm.info.usageInfoRequired = sa.getInt(
+                com.android.internal.R.styleable.AndroidManifestPermission_usageInfoRequired, 0)
+                != 0;
+
         sa.recycle();
 
         if (perm.info.protectionLevel == -1) {
@@ -5347,6 +5397,11 @@
             s.info.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
         }
         if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestService_useAppZygote,
+                false)) {
+            s.info.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
+        }
+        if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestService_singleUser,
                 false)) {
             s.info.flags |= ServiceInfo.FLAG_SINGLE_USER;
@@ -6374,6 +6429,9 @@
         @UnsupportedAppUsage
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
 
+        public final ArrayList<UsesPermissionInfo> usesPermissionInfos =
+                new ArrayList<>();
+
         /** Permissions requested but not in the manifest. */
         public final ArrayList<String> implicitPermissions = new ArrayList<>();
 
@@ -6779,7 +6837,7 @@
 
         /** @hide */
         public boolean isForwardLocked() {
-            return applicationInfo.isForwardLocked();
+            return false;
         }
 
         /** @hide */
@@ -6821,9 +6879,7 @@
         public boolean canHaveOatDir() {
             // The following app types CANNOT have oat directory
             // - non-updated system apps
-            // - forward-locked apps or apps installed in ASEC containers
-            return (!isSystem() || isUpdatedSystemApp())
-                    && !isForwardLocked() && !applicationInfo.isExternalAsec();
+            return !isSystem() || isUpdatedSystemApp();
         }
 
         public boolean isMatch(int flags) {
@@ -6906,6 +6962,7 @@
 
             dest.readStringList(requestedPermissions);
             internStringArrayList(requestedPermissions);
+            dest.readParcelableList(usesPermissionInfos, boot);
             dest.readStringList(implicitPermissions);
             internStringArrayList(implicitPermissions);
             protectedBroadcasts = dest.createStringArrayList();
@@ -7072,6 +7129,7 @@
             dest.writeParcelableList(instrumentation, flags);
 
             dest.writeStringList(requestedPermissions);
+            dest.writeParcelableList(usesPermissionInfos, flags);
             dest.writeStringList(implicitPermissions);
             dest.writeStringList(protectedBroadcasts);
 
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index e21c33a..be6ed51 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,7 +21,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -130,9 +129,6 @@
      * </p>
      */
     public boolean isMatch(ComponentInfo componentInfo, int flags) {
-        if ((flags & MATCH_ALL) != 0) {
-            return true;
-        }
         final boolean isSystemApp = componentInfo.applicationInfo.isSystemApp();
         final boolean matchUninstalled = (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0;
         if (!isAvailable(flags)
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 60c06a1..d9d6b5f 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -308,6 +309,12 @@
      */
     public CharSequence nonLocalizedDescription;
 
+    /**
+     * If {@code true} an application targeting {@link Build.VERSION_CODES#Q} <em>must</em>
+     * include permission data usage information in order to be able to be granted this permission.
+     */
+    public boolean usageInfoRequired;
+
     /** @hide */
     public static int fixProtectionLevel(int level) {
         if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -394,6 +401,7 @@
         descriptionRes = orig.descriptionRes;
         requestRes = orig.requestRes;
         nonLocalizedDescription = orig.nonLocalizedDescription;
+        usageInfoRequired = orig.usageInfoRequired;
     }
 
     /**
@@ -458,6 +466,7 @@
         dest.writeInt(descriptionRes);
         dest.writeInt(requestRes);
         TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
+        dest.writeInt(usageInfoRequired ? 1 : 0);
     }
 
     /** @hide */
@@ -498,5 +507,6 @@
         descriptionRes = source.readInt();
         requestRes = source.readInt();
         nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        usageInfoRequired = source.readInt() != 0;
     }
 }
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 91f884c..ad2c72274 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -56,6 +56,23 @@
     public static final int FLAG_EXTERNAL_SERVICE = 0x0004;
 
     /**
+     * Bit in {@link #flags}: If set, the service (which must be isolated)
+     * will be spawned from an Application Zygote, instead of the regular Zygote.
+     * The Application Zygote will pre-initialize the application's class loader,
+     * and call a static callback into the application to allow it to perform
+     * application-specific preloads (such as loading a shared library). Therefore,
+     * spawning from the Application Zygote will typically reduce the service
+     * launch time and reduce its memory usage. The downside of using this flag
+     * is that you will have an additional process (the app zygote itself) that
+     * is taking up memory. Whether actual memory usage is improved therefore
+     * strongly depends on the number of isolated services that an application
+     * starts, and how much memory those services save by preloading. Therefore,
+     * it is recommended to measure memory usage under typical workloads to
+     * determine whether it makes sense to use this flag.
+     */
+    public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
+
+    /**
      * Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
      * @hide
      */
diff --git a/core/java/android/content/pm/UsesPermissionInfo.java b/core/java/android/content/pm/UsesPermissionInfo.java
new file mode 100644
index 0000000..d08548f
--- /dev/null
+++ b/core/java/android/content/pm/UsesPermissionInfo.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.RetentionPolicy;
+/**
+ * Information you can retrive about a particular application requested permission. This
+ * corresponds to information collected from the AndroidManifest.xml's &lt;uses-permission&gt;
+ * tags.
+ */
+public final class UsesPermissionInfo extends PackageItemInfo implements Parcelable {
+
+    /**
+     * Flag for {@link #getFlags()}: the requested permission is currently granted to the
+     * application.
+     */
+    public static final int FLAG_REQUESTED_PERMISSION_GRANTED = 1 << 1;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_REQUESTED_PERMISSION_GRANTED})
+    @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /** An unset value for {@link #getDataSentOffDevice()},
+     * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+     */
+    public static final int USAGE_UNDEFINED = 0;
+
+    /**
+     * A yes value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+     * and {@link #getDataUsedForMonetization()} corresponding to the <code>yes</code> value of
+     * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+     * and {@link android.R.attr#dataUsedForMonetization} attributes.
+     */
+    public static final int USAGE_YES = 1;
+
+    /**
+     * A user triggered only value for {@link #getDataSentOffDevice()},
+     * {@link #getDataSharedWithThirdParty()}, and {@link #getDataUsedForMonetization()}
+     * corresponding to the <code>userTriggered</code> value of
+     * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+     * and {@link android.R.attr#dataUsedForMonetization} attributes.
+     */
+    public static final int USAGE_USER_TRIGGERED = 2;
+
+    /**
+     * A no value for {@link #getDataSentOffDevice()}, {@link #getDataSharedWithThirdParty()},
+     * and {@link #getDataUsedForMonetization()} corresponding to the <code>no</code> value of
+     * {@link android.R.attr#dataSentOffDevice}, {@link android.R.attr#dataSharedWithThirdParty},
+     * and {@link android.R.attr#dataUsedForMonetization} attributes.
+     */
+    public static final int USAGE_NO = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"USAGE_"}, value = {
+        USAGE_UNDEFINED,
+        USAGE_YES,
+        USAGE_USER_TRIGGERED,
+        USAGE_NO})
+    @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+    public @interface Usage {}
+
+    /**
+     * An unset value for {@link #getDataRetention}.
+     */
+    public static final int RETENTION_UNDEFINED = 0;
+
+    /**
+     * A data not retained value for {@link #getDataRetention()} corresponding to the
+     * <code>notRetained</code> value of {@link android.R.attr#dataRetentionTime}.
+     */
+    public static final int RETENTION_NOT_RETAINED = 1;
+
+    /**
+     * A user selected value for {@link #getDataRetention()} corresponding to the
+     * <code>userSelected</code> value of {@link android.R.attr#dataRetentionTime}.
+     */
+    public static final int RETENTION_USER_SELECTED = 2;
+
+    /**
+     * An unlimited value for {@link #getDataRetention()} corresponding to the
+     * <code>unlimited</code> value of {@link android.R.attr#dataRetentionTime}.
+     */
+    public static final int RETENTION_UNLIMITED = 3;
+
+    /**
+     * A specified value for {@link #getDataRetention()} corresponding to providing the number of
+     * weeks data is retained in {@link android.R.attr#dataRetentionTime}. The number of weeks
+     * is available in {@link #getDataRetentionWeeks()}.
+     */
+    public static final int RETENTION_SPECIFIED = 4;
+
+    /** @hide */
+    @IntDef(prefix = {"RETENTION_"}, value = {
+        RETENTION_UNDEFINED,
+        RETENTION_NOT_RETAINED,
+        RETENTION_USER_SELECTED,
+        RETENTION_UNLIMITED,
+        RETENTION_SPECIFIED})
+    @java.lang.annotation.Retention(RetentionPolicy.SOURCE)
+    public @interface Retention {}
+
+    private final String mPermission;
+    private final @Flags int mFlags;
+    private final @Usage int mDataSentOffDevice;
+    private final @Usage int mDataSharedWithThirdParty;
+    private final @Usage int mDataUsedForMonetization;
+    private final @Retention int mDataRetention;
+    private final int mDataRetentionWeeks;
+
+    /** @hide */
+    public UsesPermissionInfo(String permission) {
+        mPermission = permission;
+        mDataSentOffDevice = USAGE_UNDEFINED;
+        mDataSharedWithThirdParty = USAGE_UNDEFINED;
+        mDataUsedForMonetization = USAGE_UNDEFINED;
+        mDataRetention = RETENTION_UNDEFINED;
+        mDataRetentionWeeks = -1;
+        mFlags = 0;
+    }
+
+    /** @hide */
+    public UsesPermissionInfo(String permission,
+            @Usage int dataSentOffDevice, @Usage int dataSharedWithThirdParty,
+            @Usage int dataUsedForMonetization, @Retention int dataRetention,
+            int dataRetentionWeeks) {
+        mPermission = permission;
+        mDataSentOffDevice = dataSentOffDevice;
+        mDataSharedWithThirdParty = dataSharedWithThirdParty;
+        mDataUsedForMonetization = dataUsedForMonetization;
+        mDataRetention = dataRetention;
+        mDataRetentionWeeks = dataRetentionWeeks;
+        mFlags = 0;
+    }
+
+    /** @hide */
+    public UsesPermissionInfo(UsesPermissionInfo orig) {
+        this(orig, orig.mFlags);
+    }
+
+    /** @hide */
+    public UsesPermissionInfo(UsesPermissionInfo orig, int flags) {
+        super(orig);
+        mPermission = orig.mPermission;
+        mFlags = flags;
+        mDataSentOffDevice = orig.mDataSentOffDevice;
+        mDataSharedWithThirdParty = orig.mDataSharedWithThirdParty;
+        mDataUsedForMonetization = orig.mDataUsedForMonetization;
+        mDataRetention = orig.mDataRetention;
+        mDataRetentionWeeks = orig.mDataRetentionWeeks;
+    }
+
+    /**
+     * The name of the requested permission.
+     */
+    public String getPermission() {
+        return mPermission;
+    }
+
+    public @Flags int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * If the application sends the data guarded by this permission off the device.
+     *
+     * See {@link android.R.attr#dataSentOffDevice}
+     */
+    public @Usage int getDataSentOffDevice() {
+        return mDataSentOffDevice;
+    }
+
+    /**
+     * If the application or its services shares the data guarded by this permission with third
+     * parties.
+     *
+     * See {@link android.R.attr#dataSharedWithThirdParty}
+     */
+    public @Usage int getDataSharedWithThirdParty() {
+        return mDataSharedWithThirdParty;
+    }
+
+    /**
+     * If the application or its services use the data guarded by this permission for monetization
+     * purposes.
+     *
+     * See {@link android.R.attr#dataUsedForMonetization}
+     */
+    public @Usage int getDataUsedForMonetization() {
+        return mDataUsedForMonetization;
+    }
+
+    /**
+     * How long the application or its services store the data guarded by this permission.
+     * If set to {@link #RETENTION_SPECIFIED} {@link #getDataRetentionWeeks()} will contain the
+     * number of weeks the data is stored.
+     *
+     * See {@link android.R.attr#dataRetentionTime}
+     */
+    public @Retention int getDataRetention() {
+        return mDataRetention;
+    }
+
+    /**
+     * If {@link #getDataRetention()} is {@link #RETENTION_SPECIFIED} the number of weeks the
+     * application or its services store data guarded by this permission.
+     *
+     * @throws IllegalStateException if {@link #getDataRetention} is not
+     * {@link #RETENTION_SPECIFIED}.
+     */
+    public int getDataRetentionWeeks() {
+        if (mDataRetention != RETENTION_SPECIFIED) {
+            throw new IllegalStateException("Data retention weeks not specified");
+        }
+        return mDataRetentionWeeks;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mPermission);
+        dest.writeInt(mFlags);
+        dest.writeInt(mDataSentOffDevice);
+        dest.writeInt(mDataSharedWithThirdParty);
+        dest.writeInt(mDataUsedForMonetization);
+        dest.writeInt(mDataRetention);
+        dest.writeInt(mDataRetentionWeeks);
+    }
+
+    private UsesPermissionInfo(Parcel source) {
+        super(source);
+        mPermission = source.readString();
+        mFlags = source.readInt();
+        mDataSentOffDevice = source.readInt();
+        mDataSharedWithThirdParty = source.readInt();
+        mDataUsedForMonetization = source.readInt();
+        mDataRetention = source.readInt();
+        mDataRetentionWeeks = source.readInt();
+    }
+
+    public static final Creator<UsesPermissionInfo> CREATOR =
+            new Creator<UsesPermissionInfo>() {
+                @Override
+                public UsesPermissionInfo createFromParcel(Parcel source) {
+                    return new UsesPermissionInfo(source);
+                }
+                @Override
+                public UsesPermissionInfo[] newArray(int size) {
+                    return new UsesPermissionInfo[size];
+                }
+            };
+}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 79bc9a3..73addb7 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -16,13 +16,15 @@
 
 package android.content.pm.permission;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -32,7 +34,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -109,13 +111,11 @@
      */
     public void getAppPermissions(@NonNull String packageName,
             @NonNull OnResultCallback callback, @Nullable Handler handler) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = packageName;
-        args.arg2 = callback;
-        args.arg3 = handler;
-        Message message = mRemoteService.obtainMessage(
-                RemoteService.MSG_GET_APP_PERMISSIONS, args);
-        mRemoteService.processMessage(message);
+        checkNotNull(packageName);
+        checkNotNull(callback);
+
+        mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
+                mRemoteService, packageName, callback, handler));
     }
 
     /**
@@ -124,24 +124,20 @@
      * @param packageName The package for which to revoke
      * @param permissionName The permission to revoke
      */
-    public void revokeRuntimePermission(String packageName, String permissionName) {
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = packageName;
-        args.arg2 = permissionName;
+    public void revokeRuntimePermission(@NonNull String packageName,
+            @NonNull String permissionName) {
+        checkNotNull(packageName);
+        checkNotNull(permissionName);
 
-        Message message = mRemoteService.obtainMessage(
-                RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
-        mRemoteService.processMessage(message);
+        mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
+                mRemoteService, packageName, permissionName));
     }
 
     private static final class RemoteService
             extends Handler implements ServiceConnection {
         private static final long UNBIND_TIMEOUT_MILLIS = 10000;
 
-        public static final int MSG_GET_APP_PERMISSIONS = 1;
-        public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
-        public static final int MSG_UNBIND = 3;
-        public static final int MSG_REVOKE_APP_PERMISSIONS = 4;
+        public static final int MSG_UNBIND = 0;
 
         private final Object mLock = new Object();
 
@@ -191,82 +187,57 @@
             }
         }
 
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_GET_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    final String packageName = (String) args.arg1;
-                    final OnResultCallback callback = (OnResultCallback) args.arg2;
-                    final Handler handler = (Handler) args.arg3;
-                    args.recycle();
-                    final IRuntimePermissionPresenter remoteInstance;
-                    synchronized (mLock) {
-                        remoteInstance = mRemoteInstance;
-                    }
-                    if (remoteInstance == null) {
-                        return;
-                    }
-                    try {
-                        remoteInstance.getAppPermissions(packageName,
-                                new RemoteCallback(new RemoteCallback.OnResultListener() {
-                            @Override
-                            public void onResult(Bundle result) {
-                                final List<RuntimePermissionPresentationInfo> reportedPermissions;
-                                List<RuntimePermissionPresentationInfo> permissions = null;
-                                if (result != null) {
-                                    permissions = result.getParcelableArrayList(KEY_RESULT);
-                                }
-                                if (permissions == null) {
-                                    permissions = Collections.emptyList();
-                                }
-                                reportedPermissions = permissions;
-                                if (handler != null) {
-                                    handler.post(new Runnable() {
-                                        @Override
-                                        public void run() {
-                                            callback.onGetAppPermissions(reportedPermissions);
-                                        }
-                                    });
-                                } else {
-                                    callback.onGetAppPermissions(reportedPermissions);
-                                }
+        private void getAppPermissions(@NonNull String packageName,
+                @NonNull OnResultCallback callback, @Nullable Handler handler) {
+            final IRuntimePermissionPresenter remoteInstance;
+            synchronized (mLock) {
+                remoteInstance = mRemoteInstance;
+            }
+            if (remoteInstance == null) {
+                return;
+            }
+            try {
+                remoteInstance.getAppPermissions(packageName,
+                        new RemoteCallback(result -> {
+                            final List<RuntimePermissionPresentationInfo> reportedPermissions;
+                            List<RuntimePermissionPresentationInfo> permissions = null;
+                            if (result != null) {
+                                permissions = result.getParcelableArrayList(KEY_RESULT);
+                            }
+                            if (permissions == null) {
+                                permissions = Collections.emptyList();
+                            }
+                            reportedPermissions = permissions;
+                            if (handler != null) {
+                                handler.post(
+                                        () -> callback.onGetAppPermissions(reportedPermissions));
+                            } else {
+                                callback.onGetAppPermissions(reportedPermissions);
                             }
                         }, this));
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Error getting app permissions", re);
-                    }
-                    scheduleUnbind();
-                } break;
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error getting app permissions", re);
+            }
+            scheduleUnbind();
 
-                case MSG_UNBIND: {
-                    synchronized (mLock) {
-                        if (mBound) {
-                            mContext.unbindService(this);
-                            mBound = false;
-                        }
-                        mRemoteInstance = null;
-                    }
-                } break;
+            synchronized (mLock) {
+                scheduleNextMessageIfNeededLocked();
+            }
+        }
 
-                case MSG_REVOKE_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    final String packageName = (String) args.arg1;
-                    final String permissionName = (String) args.arg2;
-                    args.recycle();
-                    final IRuntimePermissionPresenter remoteInstance;
-                    synchronized (mLock) {
-                        remoteInstance = mRemoteInstance;
-                    }
-                    if (remoteInstance == null) {
-                        return;
-                    }
-                    try {
-                        remoteInstance.revokeRuntimePermission(packageName, permissionName);
-                    } catch (RemoteException re) {
-                        Log.e(TAG, "Error getting app permissions", re);
-                    }
-                } break;
+        private void revokeAppPermissions(@NonNull String packageName,
+                @NonNull String permissionName) {
+            final IRuntimePermissionPresenter remoteInstance;
+            synchronized (mLock) {
+                remoteInstance = mRemoteInstance;
+            }
+            if (remoteInstance == null) {
+                return;
+            }
+            try {
+                remoteInstance.revokeRuntimePermission(packageName, permissionName);
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error getting app permissions", re);
             }
 
             synchronized (mLock) {
@@ -274,6 +245,16 @@
             }
         }
 
+        private void unbind() {
+            synchronized (mLock) {
+                if (mBound) {
+                    mContext.unbindService(this);
+                    mBound = false;
+                }
+                mRemoteInstance = null;
+            }
+        }
+
         @GuardedBy("mLock")
         private void scheduleNextMessageIfNeededLocked() {
             if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
@@ -284,7 +265,8 @@
 
         private void scheduleUnbind() {
             removeMessages(MSG_UNBIND);
-            sendEmptyMessageDelayed(MSG_UNBIND, UNBIND_TIMEOUT_MILLIS);
+            sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
+                    .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
         }
     }
 }
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 4371c77..740cdae 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -58,7 +58,7 @@
 public final class AssetManager implements AutoCloseable {
     private static final String TAG = "AssetManager";
     private static final boolean DEBUG_REFS = false;
-    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+    private static final boolean FEATURE_FLAG_IDMAP2 = true;
 
     private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
 
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index 79e15a7a..0ec812f 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -30,19 +30,29 @@
 public interface BiometricAuthenticator {
 
     /**
+     * No biometric methods or nothing has been enrolled.
+     * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+     * modalities when calling authenticate().
      * @hide
      */
-    int TYPE_FINGERPRINT = 1;
+    int TYPE_NONE = 0;
+    /**
+     * Constant representing fingerprint.
+     * @hide
+     */
+    int TYPE_FINGERPRINT = 1 << 0;
 
     /**
+     * Constant representing iris.
      * @hide
      */
-    int TYPE_IRIS = 2;
+    int TYPE_IRIS = 1 << 1;
 
     /**
+     * Constant representing face.
      * @hide
      */
-    int TYPE_FACE = 3;
+    int TYPE_FACE = 1 << 2;
 
     /**
      * Container for biometric data
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index bd149fd..b238d77 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -251,9 +251,40 @@
     private Executor mExecutor;
     private AuthenticationCallback mAuthenticationCallback;
 
-    IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
+    private final IBiometricServiceReceiver mBiometricServiceReceiver =
+            new IBiometricServiceReceiver.Stub() {
+
         @Override
-        public void onDialogDismissed(int reason) {
+        public void onAuthenticationSucceeded() throws RemoteException {
+            mExecutor.execute(() -> {
+                final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+                mAuthenticationCallback.onAuthenticationSucceeded(result);
+            });
+        }
+
+        @Override
+        public void onAuthenticationFailed() throws RemoteException {
+            mExecutor.execute(() -> {
+                mAuthenticationCallback.onAuthenticationFailed();
+            });
+        }
+
+        @Override
+        public void onError(int error, String message) throws RemoteException {
+            mExecutor.execute(() -> {
+                mAuthenticationCallback.onAuthenticationError(error, message);
+            });
+        }
+
+        @Override
+        public void onAcquired(int acquireInfo, String message) throws RemoteException {
+            mExecutor.execute(() -> {
+                mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
+            });
+        }
+
+        @Override
+        public void onDialogDismissed(int reason) throws RemoteException {
             // Check the reason and invoke OnClickListener(s) if necessary
             if (reason == DISMISSED_REASON_POSITIVE) {
                 mPositiveButtonInfo.executor.execute(() -> {
@@ -267,40 +298,6 @@
         }
     };
 
-    IBiometricServiceReceiver mBiometricServiceReceiver =
-            new IBiometricServiceReceiver.Stub() {
-
-        @Override
-        public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
-            mExecutor.execute(() -> {
-                final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
-                mAuthenticationCallback.onAuthenticationSucceeded(result);
-            });
-        }
-
-        @Override
-        public void onAuthenticationFailed(long deviceId) throws RemoteException {
-            mExecutor.execute(() -> {
-                mAuthenticationCallback.onAuthenticationFailed();
-            });
-        }
-
-        @Override
-        public void onError(long deviceId, int error, String message)
-                throws RemoteException {
-            mExecutor.execute(() -> {
-                mAuthenticationCallback.onAuthenticationError(error, message);
-            });
-        }
-
-        @Override
-        public void onAcquired(long deviceId, int acquireInfo, String message) {
-            mExecutor.execute(() -> {
-                mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
-            });
-        }
-    };
-
     private BiometricPrompt(Context context, Bundle bundle,
             ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
         mContext = context;
@@ -557,9 +554,8 @@
             mExecutor = executor;
             mAuthenticationCallback = callback;
             final long sessionId = crypto != null ? crypto.getOpId() : 0;
-            mService.authenticate(mToken, sessionId, userId,
-                    mBiometricServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
-                    mBundle, mDialogReceiver);
+            mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver,
+                    mContext.getOpPackageName(), mBundle);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception while authenticating", e);
             mExecutor.execute(() -> {
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index e17feff..53a0761 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -18,7 +18,6 @@
 
 import android.os.Bundle;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 
 /**
@@ -32,8 +31,7 @@
     // Requests authentication. The service choose the appropriate biometric to use, and show
     // the corresponding BiometricDialog.
     void authenticate(IBinder token, long sessionId, int userId,
-            IBiometricServiceReceiver receiver, int flags, String opPackageName,
-            in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+            IBiometricServiceReceiver receiver, String opPackageName, in Bundle bundle);
 
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
@@ -46,4 +44,8 @@
 
     // Explicitly set the active user.
     void setActiveUser(int userId);
+
+    // Notify BiometricService when <Biometric>Service is ready to start the prepared client.
+    // Client lifecycle is still managed in <Biometric>Service.
+    void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index a6e3696..22ef33e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -16,12 +16,18 @@
 package android.hardware.biometrics;
 
 /**
- * Communication channel from the BiometricService back to BiometricPrompt.
+ * Communication channel from BiometricService back to BiometricPrompt
  * @hide
  */
 oneway interface IBiometricServiceReceiver {
-    void onAuthenticationSucceeded(long deviceId);
-    void onAuthenticationFailed(long deviceId);
-    void onError(long deviceId, int error, String message);
-    void onAcquired(long deviceId, int acquiredInfo, String message);
+    // Notify BiometricPrompt that authentication was successful
+    void onAuthenticationSucceeded();
+    // Noties that authentication failed.
+    void onAuthenticationFailed();
+    // Notify BiometricPrompt that an error has occurred.
+    void onError(int error, String message);
+    // Notifies that a biometric has been acquired.
+    void onAcquired(int acquiredInfo, String message);
+    // Notifies that the SystemUI dialog has been dismissed.
+    void onDialogDismissed(int reason);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
new file mode 100644
index 0000000..180daaf
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.hardware.biometrics;
+
+/**
+ * Communication channel from
+ *   1) BiometricDialogImpl (SysUI) back to BiometricService
+ *   2) <Biometric>Service back to BiometricService
+ * Receives messages from the above and does some handling before forwarding to BiometricPrompt
+ * via IBiometricServiceReceiver.
+ * @hide
+ */
+oneway interface IBiometricServiceReceiverInternal {
+    // Notify BiometricService that authentication was successful. If user confirmation is required,
+    // the auth token must be submitted into KeyStore.
+    void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
+    // Notify BiometricService that an error has occurred.
+    void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+    // Notify BiometricService than an error has occured. Forward to the correct receiver depending
+    // on the cookie.
+    void onError(int cookie, int error, String message);
+    // Notifies that a biometric has been acquired.
+    void onAcquired(int acquiredInfo, String message);
+    // Notifies that the SystemUI dialog has been dismissed.
+    void onDialogDismissed(int reason);
+    // Notifies that the user has pressed the "try again" button on SystemUI
+    void onTryAgainPressed();
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7148b12..5e402c7 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -393,7 +393,7 @@
      *         doesn't have any recommendation for this use case or the recommended configurations
      *         are invalid.
      */
-    public RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
+    public @Nullable RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(
             @RecommendedStreamConfigurationMap.RecommendedUsecase int usecase) {
         if (((usecase >= RecommendedStreamConfigurationMap.USECASE_PREVIEW) &&
                 (usecase <= RecommendedStreamConfigurationMap.USECASE_RAW)) ||
diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
index 59e4a33..068c0ce 100644
--- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2.params;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.ImageFormat;
@@ -173,7 +174,7 @@
      *
      * @return Use case id.
      */
-    public int getRecommendedUseCase() {
+    public @RecommendedUsecase int getRecommendedUseCase() {
         return mUsecase;
     }
 
@@ -326,7 +327,7 @@
      * @throws IllegalArgumentException if input size does not exist in the return value of
      *             getHighSpeedVideoSizes
      */
-    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(Size size) {
+    public @Nullable Set<Range<Integer>> getHighSpeedVideoFpsRangesFor(@NonNull Size size) {
         return getUnmodifiableRangeSet(mRecommendedMap.getHighSpeedVideoFpsRangesFor(size));
     }
 
@@ -349,14 +350,14 @@
      * For further information refer to {@link StreamConfigurationMap#getHighSpeedVideoSizesFor}.
      * </p>
      *
-     * @param fpsRange one of the FPS range returned by {@link #getHighSpeedVideoFpsRanges()}
+     * @param fpsRange one of the FPS ranges returned by {@link #getHighSpeedVideoFpsRanges()}
      * @return A non-modifiable set of video sizes to create high speed capture sessions for high
      *         speed streaming use cases.
      *
      * @throws IllegalArgumentException if input FPS range does not exist in the return value of
      *         getHighSpeedVideoFpsRanges
      */
-    public @Nullable Set<Size> getHighSpeedVideoSizesFor(Range<Integer> fpsRange) {
+    public @Nullable Set<Size> getHighSpeedVideoSizesFor(@NonNull Range<Integer> fpsRange) {
         return getUnmodifiableSizeSet(mRecommendedMap.getHighSpeedVideoSizesFor(fpsRange));
     }
 
@@ -390,10 +391,8 @@
      *          0 if the minimum frame duration is not available.
      *
      * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} was {@code null}
-     *
      */
-    public long getOutputMinFrameDuration(int format, Size size) {
+    public @IntRange(from = 0) long getOutputMinFrameDuration(int format, @NonNull Size size) {
         return mRecommendedMap.getOutputMinFrameDuration(format, size);
     }
 
@@ -409,9 +408,8 @@
      * @return a stall duration {@code >=} 0 in nanoseconds
      *
      * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} was {@code null}
      */
-    public long getOutputStallDuration(int format, Size size) {
+    public @IntRange(from = 0) long getOutputStallDuration(int format, @NonNull Size size) {
         return mRecommendedMap.getOutputStallDuration(format, size);
     }
 
@@ -422,16 +420,12 @@
      * </p>
      *
      * @param klass
-     *          a non-{@code null} {@link Class} object reference
+     *          a {@link Class} object reference
      * @return
      *          a non-modifiable set of supported sizes for {@link ImageFormat#PRIVATE} format,
      *          or {@code null} if the {@code klass} is not a supported output.
-     *
-     *
-     * @throws NullPointerException if {@code klass} was {@code null}
-     *
      */
-    public <T> @Nullable Set<Size> getOutputSizes(Class<T> klass) {
+    public <T> @Nullable Set<Size> getOutputSizes(@NonNull Class<T> klass) {
         if (mSupportsPrivate) {
             return getUnmodifiableSizeSet(mRecommendedMap.getOutputSizes(klass));
         }
@@ -447,16 +441,15 @@
      * {@link StreamConfigurationMap#getOutputMinFrameDuration(Class, Size)}.</p>
      *
      * @param klass
-     *          a class which  has a non-empty array returned by {@link #getOutputSizes(Class)}
+     *          a class which has a non-empty array returned by {@link #getOutputSizes(Class)}
      * @param size an output-compatible size
      * @return a minimum frame duration {@code >} 0 in nanoseconds, or
      *          0 if the minimum frame duration is not available.
      *
      * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
-     *
      */
-    public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) {
+    public <T> @IntRange(from = 0) long getOutputMinFrameDuration(@NonNull final Class<T> klass,
+            @NonNull final Size size) {
         if (mSupportsPrivate) {
             return mRecommendedMap.getOutputMinFrameDuration(klass, size);
         }
@@ -473,14 +466,13 @@
      * @param klass
      *          a class which has a non-empty array returned by {@link #getOutputSizes(Class)}.
      * @param size an output-compatible size
-     * @return a minimum frame duration {@code >=} 0 in nanoseconds, or 0 if the stall duration is
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or 0 if the stall duration is
      *         not available.
      *
      * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
-     * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
-     *
      */
-    public <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+    public <T> @IntRange(from = 0) long getOutputStallDuration(@NonNull final Class<T> klass,
+            @NonNull final Size size) {
         if (mSupportsPrivate) {
             return mRecommendedMap.getOutputStallDuration(klass, size);
         }
@@ -495,14 +487,13 @@
      * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}.
      * </p>
      *
-     * @param surface a non-{@code null} {@link Surface} object reference
+     * @param surface a {@link Surface} object reference
      * @return {@code true} if this is supported, {@code false} otherwise
      *
-     * @throws NullPointerException if {@code surface} was {@code null}
      * @throws IllegalArgumentException if the Surface endpoint is no longer valid
      *
      */
-    public boolean isOutputSupportedFor(Surface surface) {
+    public boolean isOutputSupportedFor(@NonNull Surface surface) {
         return mRecommendedMap.isOutputSupportedFor(surface);
     }
 
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9db1f92..9d61f02 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -24,7 +24,7 @@
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
-import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 
 /**
  * Display manager local system service interface.
@@ -126,7 +126,7 @@
      * Called by the window manager to perform traversals while holding a
      * surface flinger transaction.
      */
-    public abstract void performTraversal(SurfaceControl.Transaction t);
+    public abstract void performTraversal(Transaction t);
 
     /**
      * Tells the display manager about properties of the display that depend on the windows on it.
@@ -383,6 +383,6 @@
      * update the position of its surfaces as part of the same transaction.
      */
     public interface DisplayTransactionListener {
-        void onDisplayTransaction();
+        void onDisplayTransaction(Transaction t);
     }
 }
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index df0d46b..f2c50b5 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -19,6 +19,7 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.text.TextUtils;
 
@@ -71,6 +72,9 @@
     // The ID used to uniquely identify this display.
     public String uniqueId;
 
+    // The physical port that the associated display device is connected to.
+    public @Nullable Byte physicalPort;
+
     public @ViewportType int type;
 
     public void copyFrom(DisplayViewport viewport) {
@@ -82,6 +86,7 @@
         deviceWidth = viewport.deviceWidth;
         deviceHeight = viewport.deviceHeight;
         uniqueId = viewport.uniqueId;
+        physicalPort = viewport.physicalPort;
         type = viewport.type;
     }
 
@@ -113,6 +118,7 @@
               && deviceWidth == other.deviceWidth
               && deviceHeight == other.deviceHeight
               && TextUtils.equals(uniqueId, other.uniqueId)
+              && physicalPort == other.physicalPort
               && type == other.type;
     }
 
@@ -128,6 +134,7 @@
         result += prime * result + deviceWidth;
         result += prime * result + deviceHeight;
         result += prime * result + uniqueId.hashCode();
+        result += prime * result + physicalPort;
         result += prime * result + type;
         return result;
     }
@@ -139,6 +146,7 @@
                 + ", valid=" + valid
                 + ", displayId=" + displayId
                 + ", uniqueId='" + uniqueId + "'"
+                + ", physicalPort=" + physicalPort
                 + ", orientation=" + orientation
                 + ", logicalFrame=" + logicalFrame
                 + ", physicalFrame=" + physicalFrame
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 47df8e8..a15dcec 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -15,9 +15,7 @@
  */
 package android.hardware.face;
 
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.face.IFaceServiceReceiver;
 import android.hardware.face.Face;
@@ -32,19 +30,24 @@
     void authenticate(IBinder token, long sessionId,
             IFaceServiceReceiver receiver, int flags, String opPackageName);
 
-    // This method invokes the BiometricDialog. The arguments are almost the same as above,
-    // but should only be called from (BiometricPromptService).
-    void authenticateFromService(boolean requireConfirmation, IBinder token, long sessionId,
-            int userId, IBiometricServiceReceiver receiver, int flags, String opPackageName,
-            in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
-            int callingUid, int callingPid, int callingUserId);
+    // This method prepares the service to start authenticating, but doesn't start authentication.
+    // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+    // called from BiometricService. The additional uid, pid, userId arguments should be determined
+    // by BiometricService. To start authentication after the clients are ready, use
+    // startPreparedClient().
+    void prepareForAuthentication(boolean requireConfirmation, IBinder token, long sessionId,
+            int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+            int cookie, int callingUid, int callingPid, int callingUserId);
+
+    // Starts authentication with the previously prepared client.
+    void startPreparedClient(int cookie);
 
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
 
     // Same as above, with extra arguments.
     void cancelAuthenticationFromService(IBinder token, String opPackageName,
-            int callingUid, int callingPid, int callingUserId);
+            int callingUid, int callingPid, int callingUserId, boolean fromClient);
 
     // Start face enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2662a11..dd6b29d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,9 +15,7 @@
  */
 package android.hardware.fingerprint;
 
-import android.os.Bundle;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -35,22 +33,25 @@
     void authenticate(IBinder token, long sessionId, int userId,
             IFingerprintServiceReceiver receiver, int flags, String opPackageName);
 
-    // This method invokes the BiometricDialog. The arguments are almost the same as above, except
-    // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
-    // called from BiometricPromptService. The additional uid, pid, userId arguments should be
-    // determined by BiometricPromptService.
-    void authenticateFromService(IBinder token, long sessionId, int userId,
-            IBiometricServiceReceiver receiver, int flags, String opPackageName,
-            in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+    // This method prepares the service to start authenticating, but doesn't start authentication.
+    // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
+    // called from BiometricService. The additional uid, pid, userId arguments should be determined
+    // by BiometricService. To start authentication after the clients are ready, use
+    // startPreparedClient().
+    void prepareForAuthentication(IBinder token, long sessionId, int userId,
+            IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie,
             int callingUid, int callingPid, int callingUserId);
 
+    // Starts authentication with the previously prepared client.
+    void startPreparedClient(int cookie);
+
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
 
     // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
     // an additional uid, pid, userid.
     void cancelAuthenticationFromService(IBinder token, String opPackageName,
-            int callingUid, int callingPid, int callingUserId);
+            int callingUid, int callingPid, int callingUserId, boolean fromClient);
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
diff --git a/core/java/android/hardware/location/ContextHubIntentEvent.java b/core/java/android/hardware/location/ContextHubIntentEvent.java
index 539c494..d1190ab 100644
--- a/core/java/android/hardware/location/ContextHubIntentEvent.java
+++ b/core/java/android/hardware/location/ContextHubIntentEvent.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.PendingIntent;
 import android.content.Intent;
 
@@ -30,6 +31,7 @@
  *
  * @hide
  */
+@SystemApi
 public class ContextHubIntentEvent {
     @ContextHubManager.Event private final int mEventType;
 
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 88fb3de..7639302 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -56,38 +56,28 @@
 
     /**
      * An extra of type {@link ContextHubInfo} describing the source of the event.
-     *
-     * @hide
      */
     public static final String EXTRA_CONTEXT_HUB_INFO =
             "android.hardware.location.extra.CONTEXT_HUB_INFO";
 
     /**
      * An extra of type {@link ContextHubManager.Event} describing the event type.
-     *
-     * @hide
      */
     public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
 
     /**
      * An extra of type long describing the ID of the nanoapp an event is for.
-     *
-     * @hide
      */
     public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
 
     /**
      * An extra of type int describing the nanoapp-specific abort code.
-     *
-     * @hide
      */
     public static final String EXTRA_NANOAPP_ABORT_CODE =
             "android.hardware.location.extra.NANOAPP_ABORT_CODE";
 
     /**
      * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
-     *
-     * @hide
      */
     public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
 
@@ -109,56 +99,41 @@
 
     /**
      * An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_LOADED = 0;
 
     /**
      * An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_UNLOADED = 1;
 
     /**
      * An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_ENABLED = 2;
 
     /**
      * An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_DISABLED = 3;
 
     /**
      * An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
      * EXTRA_NANOAPP_ABORT_CODE extras.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_ABORTED = 4;
 
     /**
      * An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
      * EXTRA_NANOAPP_MESSAGE extras.
-     *
-     * @hide
      */
     public static final int EVENT_NANOAPP_MESSAGE = 5;
 
     /**
      * An event describing that the Context Hub has reset.
-     *
-     * @hide
      */
     public static final int EVENT_HUB_RESET = 6;
 
-
     private final Looper mMainLooper;
     private final IContextHubService mService;
     private Callback mCallback;
@@ -797,14 +772,14 @@
      * Creates a ContextHubClient that will receive notifications based on Intent events.
      *
      * This method should be used instead of {@link #createClient(ContextHubInfo,
-     * ContextHubClientCallback)} and the equivalent API if the caller wants to preserve the
-     * messaging endpoint of a ContextHubClient, even after a process exits. If the PendingIntent
-     * with the provided nanoapp has already been registered at the service previously, then the
-     * same ContextHubClient will be regenerated without creating a new client connection at the
-     * service. Note that the PendingIntent, nanoapp, and Context Hub must all match in identifying
-     * a previously registered ContextHubClient. If a client is regenerated, it can be treated as
-     * the same endpoint entity from a nanoapp's perspective, and can be continued to be
-     * used to send messages even if the original process has exited.
+     * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
+     * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
+     * after a process exits. If the PendingIntent with the provided nanoapp has already been
+     * registered at the service, then the same ContextHubClient will be regenerated without
+     * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
+     * Context Hub must all match in identifying a previously registered ContextHubClient.
+     * If a client is regenerated, the host endpoint identifier attached to messages sent to the
+     * nanoapp remains consistent, even if the original process has exited.
      *
      * If registered successfully, intents will be delivered regarding events or messages from the
      * specified nanoapp from the attached Context Hub. The intent will have an extra
@@ -815,10 +790,11 @@
      * each event type, along with event-specific extra fields. The client can also use
      * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
      *
-     * Intent events will be delivered until it is unregistered through
-     * {@link ContextHubClient.close()}. Note that the registration of this
-     * ContextHubClient at the Context Hub Service will continued to be maintained until
-     * {@link ContextHubClient.close()} is called.
+     * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
+     * the registration of this ContextHubClient at the Context Hub Service will be maintained until
+     * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
+     * on the provided PendingIntent, then the client will be automatically unregistered by the
+     * service.
      *
      * @param hubInfo       the hub to attach this client to
      * @param pendingIntent the PendingIntent to register to the client
@@ -828,8 +804,6 @@
      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
      * @throws IllegalStateException    if there were too many registered clients at the service
      * @throws NullPointerException     if pendingIntent or hubInfo is null
-     *
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     @NonNull public ContextHubClient createClient(
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 1999e78..cb82fbe 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -44,6 +44,8 @@
     public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10;
     public static final int NETWORK_REVALIDATION_PORTAL_FOUND     = 11;
 
+    public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
+
     @IntDef(value = {
             NETWORK_CONNECTED,
             NETWORK_VALIDATED,
@@ -56,6 +58,7 @@
             NETWORK_REVALIDATION_SUCCESS,
             NETWORK_FIRST_VALIDATION_PORTAL_FOUND,
             NETWORK_REVALIDATION_PORTAL_FOUND,
+            NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index c7184c0..64314a7 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -400,6 +400,9 @@
      * }
      * </pre>
      *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
      * @param workSource The original UID responsible for the binder call.
      * @return token to restore original work source.
      * @hide
@@ -423,6 +426,9 @@
     /**
      * Clears the work source on this thread.
      *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
+     *
      * @return token to restore original work source.
      * @hide
      **/
@@ -442,6 +448,9 @@
      *   Binder.restoreCallingWorkSource(token);
      * }
      * </pre>
+     *
+     * <p>The work source will be propagated for future outgoing binder transactions
+     * executed on this thread.
      * @hide
      **/
     @CriticalNative
@@ -919,7 +928,9 @@
         final long origWorkSource = ThreadLocalWorkSource.setUid(Binder.getCallingUid());
         try {
             if (tracingEnabled) {
-                Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":" + code);
+                final String transactionName = getTransactionName(code);
+                Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
+                        + (transactionName != null ? transactionName : code));
             }
             res = onTransact(code, data, reply, flags);
         } catch (RemoteException|RuntimeException e) {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 0c1aae8..8904ee6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -657,6 +657,12 @@
     public static String DIRECTORY_SCREENSHOTS = "Screenshots";
 
     /**
+     * Standard directory in which to place any audio files which are
+     * audiobooks.
+     */
+    public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
+
+    /**
      * List of standard storage directories.
      * <p>
      * Each of its values have its own constant:
@@ -671,6 +677,7 @@
      *   <li>{@link #DIRECTORY_DOWNLOADS}
      *   <li>{@link #DIRECTORY_DCIM}
      *   <li>{@link #DIRECTORY_DOCUMENTS}
+     *   <li>{@link #DIRECTORY_AUDIOBOOKS}
      * </ul>
      * @hide
      */
@@ -684,7 +691,8 @@
             DIRECTORY_MOVIES,
             DIRECTORY_DOWNLOADS,
             DIRECTORY_DCIM,
-            DIRECTORY_DOCUMENTS
+            DIRECTORY_DOCUMENTS,
+            DIRECTORY_AUDIOBOOKS,
     };
 
     /**
@@ -709,6 +717,7 @@
     /** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
     /** {@hide} */ public static final int HAS_DCIM = 1 << 8;
     /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+    /** {@hide} */ public static final int HAS_AUDIOBOOKS = 1 << 10;
 
     /** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
     /** {@hide} */ public static final int HAS_OTHER = 1 << 17;
@@ -738,6 +747,7 @@
                 else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
                 else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
                 else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+                else if (DIRECTORY_AUDIOBOOKS.equals(name)) res |= HAS_AUDIOBOOKS;
                 else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
                 else res |= HAS_OTHER;
             }
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 44b9e311..6de1ff4 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -17,12 +17,6 @@
 package android.os;
 
 import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.O_APPEND;
-import static android.system.OsConstants.O_CREAT;
-import static android.system.OsConstants.O_RDONLY;
-import static android.system.OsConstants.O_RDWR;
-import static android.system.OsConstants.O_TRUNC;
-import static android.system.OsConstants.O_WRONLY;
 import static android.system.OsConstants.SEEK_SET;
 import static android.system.OsConstants.SOCK_SEQPACKET;
 import static android.system.OsConstants.SOCK_STREAM;
@@ -254,8 +248,16 @@
     }
 
     /** {@hide} */
-    public static ParcelFileDescriptor fromFd(
-            FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException {
+    public static ParcelFileDescriptor fromPfd(ParcelFileDescriptor pfd, Handler handler,
+            final OnCloseListener listener) throws IOException {
+        final FileDescriptor original = new FileDescriptor();
+        original.setInt$(pfd.detachFd());
+        return fromFd(original, handler, listener);
+    }
+
+    /** {@hide} */
+    public static ParcelFileDescriptor fromFd(FileDescriptor fd, Handler handler,
+            final OnCloseListener listener) throws IOException {
         if (handler == null) {
             throw new IllegalArgumentException("Handler must not be null");
         }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1c1db68..894015f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1008,7 +1008,8 @@
      * progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when
      * dream ends.
      * </p><p>
-     * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission.
+     * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and
+     * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions.
      * </p>
      *
      * @param time The time when the request to nap was issued, in the
@@ -1019,7 +1020,9 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.READ_DREAM_STATE,
+            android.Manifest.permission.WRITE_DREAM_STATE })
     public void dream(long time) {
         Sandman.startDreamByUserRequest(mContext);
     }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 651caec..2abcb4c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1055,6 +1055,9 @@
      */
     public static final native long getPss(int pid);
 
+    /** @hide */
+    public static final native long[] getRss(int pid);
+
     /**
      * Specifies the outcome of having started a process.
      * @hide
diff --git a/core/java/android/os/RedactingFileDescriptor.java b/core/java/android/os/RedactingFileDescriptor.java
index 60eb5c3..4e5eaac 100644
--- a/core/java/android/os/RedactingFileDescriptor.java
+++ b/core/java/android/os/RedactingFileDescriptor.java
@@ -20,15 +20,18 @@
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
-import android.system.OsConstants;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
 
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InterruptedIOException;
+import java.util.Arrays;
 
 /**
  * Variant of {@link FileDescriptor} that allows its creator to specify regions
@@ -40,20 +43,21 @@
     private static final String TAG = "RedactingFileDescriptor";
     private static final boolean DEBUG = true;
 
-    private final long[] mRedactRanges;
+    private volatile long[] mRedactRanges;
 
     private FileDescriptor mInner = null;
     private ParcelFileDescriptor mOuter = null;
 
-    private RedactingFileDescriptor(Context context, File file, long[] redactRanges)
+    private RedactingFileDescriptor(Context context, File file, int mode, long[] redactRanges)
             throws IOException {
         mRedactRanges = checkRangesArgument(redactRanges);
 
         try {
             try {
-                mInner = Os.open(file.getAbsolutePath(), OsConstants.O_RDONLY, 0);
+                mInner = Os.open(file.getAbsolutePath(),
+                        FileUtils.translateModePfdToPosix(mode), 0);
                 mOuter = context.getSystemService(StorageManager.class)
-                        .openProxyFileDescriptor(ParcelFileDescriptor.MODE_READ_ONLY, mCallback);
+                        .openProxyFileDescriptor(mode, mCallback);
             } catch (ErrnoException e) {
                 throw e.rethrowAsIOException();
             }
@@ -78,16 +82,61 @@
 
     /**
      * Open the given {@link File} and returns a {@link ParcelFileDescriptor}
-     * that offers a redacted, read-only view of the underlying data.
+     * that offers a redacted view of the underlying data. If a redacted region
+     * is written to, the newly written data can be read back correctly instead
+     * of continuing to be redacted.
      *
      * @param file The underlying file to open.
+     * @param mode The {@link ParcelFileDescriptor} mode to open with.
      * @param redactRanges List of file offsets that should be redacted, stored
      *            as {@code [start1, end1, start2, end2, ...]}. Start values are
      *            inclusive and end values are exclusive.
      */
-    public static ParcelFileDescriptor open(Context context, File file, long[] redactRanges)
-            throws IOException {
-        return new RedactingFileDescriptor(context, file, redactRanges).mOuter;
+    public static ParcelFileDescriptor open(Context context, File file, int mode,
+            long[] redactRanges) throws IOException {
+        return new RedactingFileDescriptor(context, file, mode, redactRanges).mOuter;
+    }
+
+    /**
+     * Update the given ranges argument to remove any references to the given
+     * offset and length. This is typically used when a caller has written over
+     * a previously redacted region.
+     */
+    @VisibleForTesting
+    public static long[] removeRange(long[] ranges, long start, long end) {
+        if (start == end) {
+            return ranges;
+        } else if (start > end) {
+            throw new IllegalArgumentException();
+        }
+
+        long[] res = EmptyArray.LONG;
+        for (int i = 0; i < ranges.length; i += 2) {
+            if (start <= ranges[i] && end >= ranges[i + 1]) {
+                // Range entirely covered; remove it
+            } else if (start >= ranges[i] && end <= ranges[i + 1]) {
+                // Range partially covered; punch a hole
+                res = Arrays.copyOf(res, res.length + 4);
+                res[res.length - 4] = ranges[i];
+                res[res.length - 3] = start;
+                res[res.length - 2] = end;
+                res[res.length - 1] = ranges[i + 1];
+            } else {
+                // Range might covered; adjust edges if needed
+                res = Arrays.copyOf(res, res.length + 2);
+                if (end >= ranges[i] && end <= ranges[i + 1]) {
+                    res[res.length - 2] = Math.max(ranges[i], end);
+                } else {
+                    res[res.length - 2] = ranges[i];
+                }
+                if (start >= ranges[i] && start <= ranges[i + 1]) {
+                    res[res.length - 1] = Math.min(ranges[i + 1], start);
+                } else {
+                    res[res.length - 1] = ranges[i + 1];
+                }
+            }
+        }
+        return res;
     }
 
     private final ProxyFileDescriptorCallback mCallback = new ProxyFileDescriptorCallback() {
@@ -126,7 +175,24 @@
 
         @Override
         public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
-            throw new ErrnoException(TAG, OsConstants.EBADF);
+            int n = 0;
+            while (n < size) {
+                try {
+                    final int res = Os.pwrite(mInner, data, n, size - n, offset + n);
+                    if (res == 0) {
+                        break;
+                    } else {
+                        n += res;
+                    }
+                } catch (InterruptedIOException e) {
+                    n += e.bytesTransferred;
+                }
+            }
+
+            // Clear any relevant redaction ranges before returning, since the
+            // writer should have access to see the data they just overwrote
+            mRedactRanges = removeRange(mRedactRanges, offset, offset + n);
+            return n;
         }
 
         @Override
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 866bd9a..acb9eac 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -43,6 +43,7 @@
     int mTag;
     long mElapsedTimeNs;
     long mWallClockTimeNs;
+    WorkSource mWorkSource = null;
 
     public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
         this.mTag = tag;
@@ -71,6 +72,17 @@
             };
 
     /**
+     * Set work source if any.
+     */
+    public void setWorkSource(WorkSource ws) {
+        if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
+            Slog.w(TAG, "Empty worksource!");
+            return;
+        }
+        mWorkSource = ws;
+    }
+
+    /**
      * Write a int value.
      */
     public void writeInt(int val) {
@@ -119,11 +131,6 @@
         mValues.add(val ? 1 : 0);
     }
 
-    /**
-     * Writes the stored fields to a byte array. Will first write a new-line character to denote
-     * END_LIST before writing contents to byte array.
-     */
-
     public void writeToParcel(Parcel out, int flags) {
         if (DEBUG) {
             Slog.d(TAG,
@@ -133,6 +140,34 @@
         out.writeInt(mTag);
         out.writeLong(mElapsedTimeNs);
         out.writeLong(mWallClockTimeNs);
+        if (mWorkSource != null) {
+            ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
+            // number of chains
+            out.writeInt(workChains.size());
+            for (int i = 0; i < workChains.size(); i++) {
+                android.os.WorkSource.WorkChain wc = workChains.get(i);
+                if (wc.getSize() == 0) {
+                    Slog.w(TAG, "Empty work chain.");
+                    out.writeInt(0);
+                    continue;
+                }
+                if (wc.getUids().length != wc.getTags().length
+                        || wc.getUids().length != wc.getSize()) {
+                    Slog.w(TAG, "Malformated work chain.");
+                    out.writeInt(0);
+                    continue;
+                }
+                // number of nodes
+                out.writeInt(wc.getSize());
+                for (int j = 0; j < wc.getSize(); j++) {
+                    out.writeInt(wc.getUids()[j]);
+                    out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
+                }
+            }
+        } else {
+            // no chains
+            out.writeInt(0);
+        }
         out.writeInt(mTypes.size());
         for (int i = 0; i < mTypes.size(); i++) {
             out.writeInt(mTypes.get(i));
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 92b3169..abfcfaa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2463,6 +2463,27 @@
     }
 
     /**
+     * Get the parent of a user profile.
+     *
+     * @param user the handle of the user profile
+     *
+     * @return the parent of the user or {@code null} if the user is not profile
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
+        UserInfo info = getProfileParent(user.getIdentifier());
+
+        if (info == null) {
+            return null;
+        }
+
+        return UserHandle.of(info.id);
+    }
+
+    /**
      * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
      * managed profile don't run, generate notifications, or consume data or battery.
      * <p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b42f1c4..d315383 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.app.Activity;
@@ -55,6 +56,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -1494,7 +1496,7 @@
      * framework, so no service needs to check for changes during their lifespan
      */
     public static boolean isBlockEncrypting() {
-        final String state = SystemProperties.get("vold.encrypt_progress", "");
+        final String state = VoldProperties.encrypt_progress().orElse("");
         return !"".equalsIgnoreCase(state);
     }
 
@@ -1510,7 +1512,7 @@
      * framework, so no service needs to check for changes during their lifespan
      */
     public static boolean inCryptKeeperBounce() {
-        final String status = SystemProperties.get("vold.decrypt");
+        final String status = VoldProperties.decrypt().orElse("");
         return "trigger_restart_min_framework".equals(status);
     }
 
@@ -1533,6 +1535,12 @@
         return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false);
     }
 
+    /** {@hide} */
+    @TestApi
+    public static boolean hasIsolatedStorage() {
+        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false);
+    }
+
     /**
      * @deprecated disabled now that FUSE has been replaced by sdcardfs
      * @hide
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 18aea03..a41a644 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -16,6 +16,9 @@
 
 package android.permissionpresenterservice;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.app.Service;
@@ -27,12 +30,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteCallback;
 
-import com.android.internal.os.SomeArgs;
-
 import java.util.List;
 
 /**
@@ -63,7 +62,7 @@
     @Override
     public final void attachBaseContext(Context base) {
         super.attachBaseContext(base);
-        mHandler = new MyHandler(base.getMainLooper());
+        mHandler = new Handler(base.getMainLooper());
     }
 
     /**
@@ -71,7 +70,8 @@
      *
      * @param packageName The package for which to query.
      */
-    public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+    public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(
+            @NonNull String packageName);
 
     /**
      * Revokes the permission {@code permissionName} for app {@code packageName}
@@ -87,61 +87,35 @@
         return new IRuntimePermissionPresenter.Stub() {
             @Override
             public void getAppPermissions(String packageName, RemoteCallback callback) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = packageName;
-                args.arg2 = callback;
-                mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
-                        args).sendToTarget();
+                checkNotNull(packageName, "packageName");
+                checkNotNull(callback, "callback");
+
+                mHandler.sendMessage(
+                        obtainMessage(RuntimePermissionPresenterService::getAppPermissions,
+                                RuntimePermissionPresenterService.this, packageName, callback));
             }
 
             @Override
             public void revokeRuntimePermission(String packageName, String permissionName) {
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = packageName;
-                args.arg2 = permissionName;
-                mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION,
-                        args).sendToTarget();
+                checkNotNull(packageName, "packageName");
+                checkNotNull(permissionName, "permissionName");
+
+                mHandler.sendMessage(
+                        obtainMessage(RuntimePermissionPresenterService::onRevokeRuntimePermission,
+                                RuntimePermissionPresenterService.this, packageName,
+                                permissionName));
             }
         };
     }
 
-    private final class MyHandler extends Handler {
-        public static final int MSG_GET_APP_PERMISSIONS = 1;
-        public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
-        public static final int MSG_REVOKE_APP_PERMISSION = 3;
-
-        public MyHandler(Looper looper) {
-            super(looper, null, false);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_GET_APP_PERMISSIONS: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String packageName = (String) args.arg1;
-                    RemoteCallback callback = (RemoteCallback) args.arg2;
-                    args.recycle();
-                    List<RuntimePermissionPresentationInfo> permissions =
-                            onGetAppPermissions(packageName);
-                    if (permissions != null && !permissions.isEmpty()) {
-                        Bundle result = new Bundle();
-                        result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT,
-                                permissions);
-                        callback.sendResult(result);
-                    } else {
-                        callback.sendResult(null);
-                    }
-                } break;
-                case MSG_REVOKE_APP_PERMISSION: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    String packageName = (String) args.arg1;
-                    String permissionName = (String) args.arg2;
-                    args.recycle();
-
-                    onRevokeRuntimePermission(packageName, permissionName);
-                } break;
-            }
+    private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
+        List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
+        if (permissions != null && !permissions.isEmpty()) {
+            Bundle result = new Bundle();
+            result.putParcelableList(RuntimePermissionPresenter.KEY_RESULT, permissions);
+            callback.sendResult(result);
+        } else {
+            callback.sendResult(null);
         }
     }
 }
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 76607e9..8e37559 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -660,7 +660,22 @@
         if (familyBuilder == null) {
             return null;
         }
-        return new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+
+        final FontFamily family = familyBuilder.build();
+
+        final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+                FontStyle.FONT_SLANT_UPRIGHT);
+        Font bestFont = family.getFont(0);
+        int bestScore = normal.getMatchScore(bestFont.getStyle());
+        for (int i = 1; i < family.getSize(); ++i) {
+            final Font candidate = family.getFont(i);
+            final int score = normal.getMatchScore(candidate.getStyle());
+            if (score < bestScore) {
+                bestFont = candidate;
+                bestScore = score;
+            }
+        }
+        return new Typeface.CustomFallbackBuilder(family).setStyle(bestFont.getStyle()).build();
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 291891e..0299e41 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -37,6 +37,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
+import android.media.ExifInterface;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -121,6 +122,8 @@
     public static final String PARAM_INCLUDE_PENDING = "includePending";
     /** {@hide} */
     public static final String PARAM_PROGRESS = "progress";
+    /** {@hide} */
+    public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
 
     /**
      * Activity Action: Launch a music player.
@@ -478,6 +481,24 @@
     }
 
     /**
+     * Update the given {@link Uri} to indicate that the caller requires the
+     * original file contents when calling
+     * {@link ContentResolver#openFileDescriptor(Uri, String)}.
+     * <p>
+     * This can be useful when the caller wants to ensure they're backing up the
+     * exact bytes of the underlying media, without any Exif redaction being
+     * performed.
+     * <p>
+     * If the original file contents cannot be provided, a
+     * {@link UnsupportedOperationException} will be thrown when the returned
+     * {@link Uri} is used, such as when the caller doesn't hold
+     * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
+     */
+    public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
+        return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
+    }
+
+    /**
      * Create a new pending media item using the given parameters. Pending items
      * are expected to have a short lifetime, and owners should either
      * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
@@ -572,6 +593,34 @@
         public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
             this.secondaryDirectory = secondaryDirectory;
         }
+
+        /**
+         * Optionally set the Uri from where the file has been downloaded. This is used
+         * for files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#DOWNLOAD_URI
+         */
+        public void setDownloadUri(@Nullable Uri downloadUri) {
+            if (downloadUri == null) {
+                this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
+            } else {
+                this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
+            }
+        }
+
+        /**
+         * Optionally set the Uri indicating HTTP referer of the file. This is used for
+         * files being added to {@link Downloads} table.
+         *
+         * @see DownloadColumns#REFERER_URI
+         */
+        public void setRefererUri(@Nullable Uri refererUri) {
+            if (refererUri == null) {
+                this.insertValues.remove(DownloadColumns.REFERER_URI);
+            } else {
+                this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
+            }
+        }
     }
 
     /**
@@ -763,7 +812,7 @@
          * Type: BOOLEAN
          *
          * @see MediaStore#createPending(Context, PendingParams)
-         * @see MediaStore#QUERY_ARG_INCLUDE_PENDING
+         * @see MediaStore#PARAM_INCLUDE_PENDING
          */
         public static final String IS_PENDING = "is_pending";
 
@@ -927,6 +976,12 @@
              * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
              */
             public static final int MEDIA_TYPE_PLAYLIST = 4;
+
+            /**
+             * Column indicating if the file is part of Downloads collection.
+             * @hide
+             */
+            public static final String IS_DOWNLOAD = "is_download";
         }
     }
 
@@ -940,6 +995,80 @@
         public static final Point MICRO_SIZE = new Point(96, 96);
     }
 
+    /** Column fields for downloaded files used in {@link Downloads} table */
+    public interface DownloadColumns extends MediaColumns {
+        /**
+         * Uri indicating where the file has been downloaded from.
+         * <p>
+         * Type: TEXT
+         */
+        String DOWNLOAD_URI = "download_uri";
+
+        /**
+         * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
+         * <p>
+         * Type: TEXT
+         */
+        String REFERER_URI = "referer_uri";
+    }
+
+    /**
+     * Container for downloaded files.
+     *
+     * <p>
+     * Querying for downloads from this table will return files contributed via
+     * {@link PendingSession} and also ones which were downloaded using
+     * {@link android.app.DownloadManager} APIs.
+     */
+    public static final class Downloads implements DownloadColumns {
+        private Downloads() {}
+
+        /**
+         * The content:// style URI for the internal storage.
+         */
+        public static final Uri INTERNAL_CONTENT_URI =
+                getContentUri("internal");
+
+        /**
+         * The content:// style URI for the "primary" external storage
+         * volume.
+         */
+        public static final Uri EXTERNAL_CONTENT_URI =
+                getContentUri("external");
+
+        /**
+         * Get the content:// style URI for the downloads table on the
+         * given volume.
+         *
+         * @param volumeName the name of the volume to get the URI for
+         * @return the URI to the image media table on the given volume
+         */
+        public static Uri getContentUri(String volumeName) {
+            return AUTHORITY_URI.buildUpon().appendPath(volumeName)
+                    .appendPath("downloads").build();
+        }
+
+        /** @hide */
+        public static Uri getContentUriForPath(@NonNull String path) {
+            return getContentUri(getVolumeNameForPath(path));
+        }
+    }
+
+    private static String getVolumeNameForPath(@NonNull String path) {
+        final StorageManager sm = AppGlobals.getInitialApplication()
+                .getSystemService(StorageManager.class);
+        final StorageVolume sv = sm.getStorageVolume(new File(path));
+        if (sv != null) {
+            if (sv.isPrimary()) {
+                return VOLUME_EXTERNAL;
+            } else {
+                return sv.getUuid();
+            }
+        } else {
+            return VOLUME_INTERNAL;
+        }
+    }
+
     /**
      * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
      * to be accessed elsewhere.
@@ -1038,13 +1167,25 @@
             /**
              * The latitude where the image was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LONGITUDE = "longitude";
 
             /**
@@ -1565,6 +1706,12 @@
             public static final String IS_NOTIFICATION = "is_notification";
 
             /**
+             * Non-zero if the audio file is an audiobook
+             * <P>Type: INTEGER (boolean)</P>
+             */
+            public static final String IS_AUDIOBOOK = "is_audiobook";
+
+            /**
              * The genre of the audio file, if any
              * <P>Type: TEXT</P>
              * Does not exist in the database - only used by the media scanner for inserts.
@@ -1671,18 +1818,7 @@
              *             access this path.
              */
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
-                final StorageManager sm = AppGlobals.getInitialApplication()
-                        .getSystemService(StorageManager.class);
-                final StorageVolume sv = sm.getStorageVolume(new File(path));
-                if (sv != null) {
-                    if (sv.isPrimary()) {
-                        return EXTERNAL_CONTENT_URI;
-                    } else {
-                        return getContentUri(sv.getUuid());
-                    }
-                } else {
-                    return INTERNAL_CONTENT_URI;
-                }
+                return getContentUri(getVolumeNameForPath(path));
             }
 
             /**
@@ -2287,13 +2423,25 @@
             /**
              * The latitude where the video was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
              * <P>Type: DOUBLE</P>
+             *
+             * @deprecated location details are no longer indexed for privacy
+             *             reasons, and this value is now always {@code null}.
+             *             You can still manually obtain location metadata using
+             *             {@link ExifInterface#getLatLong(float[])}.
              */
+            @Deprecated
             public static final String LONGITUDE = "longitude";
 
             /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dbdeb70..d367afc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5075,6 +5075,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public static void resetToDefaults(@NonNull ContentResolver resolver,
                 @Nullable String tag) {
@@ -6278,6 +6279,7 @@
          *
          * @hide
          */
+        @SystemApi
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
                 "accessibility_display_magnification_navbar_enabled";
 
@@ -7967,6 +7969,14 @@
                 "managed_profile_contact_remote_search";
 
         /**
+         * Whether parent profile can access remote calendar data in managed profile.
+         *
+         * @hide
+         */
+        public static final String CROSS_PROFILE_CALENDAR_ENABLED =
+                "cross_profile_calendar_enabled";
+
+        /**
          * Whether or not the automatic storage manager is enabled and should run on the device.
          *
          * @hide
@@ -8224,6 +8234,24 @@
         public static final String NOTIFICATION_NEW_INTERRUPTION_MODEL = "new_interruption_model";
 
         /**
+         * How often to check for location access.
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
+                "location_access_check_interval_millis";
+
+        /**
+         * Delay between granting location access and checking it.
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
+                "location_access_check_delay_millis";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -10780,6 +10808,41 @@
         public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
 
         /**
+         * The threshold value for the number of consecutive dns timeout events received to be a
+         * signal of data stall. Set the value to 0 or less than 0 to disable. Note that the value
+         * should be larger than 0 if the DNS data stall detection is enabled.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
+                "data_stall_consecutive_dns_timeout_threshold";
+
+        /**
+         * The minimal time interval in milliseconds for data stall reevaluation.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_MIN_EVALUATE_INTERVAL =
+                "data_stall_min_evaluate_interval";
+
+        /**
+         * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
+         * a data stall.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD =
+                "data_stall_valid_dns_time_threshold";
+
+        /**
+         * Which data stall detection signal to use. Possible values are a union of the powers of 2
+         * of DATA_STALL_EVALUATION_TYPE_*.
+         *
+         * @hide
+         */
+        public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+
+        /**
          * Whether network service discovery is enabled.
          *
          * @hide
@@ -11000,6 +11063,16 @@
                 = "activity_starts_logging_enabled";
 
         /**
+         * Feature flag to enable or disable the background activity starts.
+         * When disabled, apps aren't allowed to start activities unless they're in the foreground.
+         * Type: int (0 for false, 1 for true)
+         * Default: 1
+         * @hide
+         */
+        public static final String BACKGROUND_ACTIVITY_STARTS_ENABLED =
+                "background_activity_starts_enabled";
+
+        /**
          * @hide
          * @see com.android.server.appbinding.AppBindingConstants
          */
@@ -11565,7 +11638,7 @@
         /**
          * Whether or not show hidden launcher icon apps feature is enabled.
          * Type: int (0 for false, 1 for true)
-         * Default: 0
+         * Default: 1
          * @hide
          */
         public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
@@ -13437,6 +13510,13 @@
          */
         public static final String WARNING_TEMPERATURE = "warning_temperature";
 
+
+        /**
+         * USB Temperature at which the high temperature alarm notification should be shown.
+         * @hide
+         */
+        public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature";
+
         /**
          * Whether the diskstats logging task is enabled/disabled.
          * @hide
@@ -13718,6 +13798,15 @@
          */
         public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
 
+
+        /**
+         * Whether we've enabled native flags health check on this device. Takes effect on
+         * reboot. The value "1" enables native flags health check; otherwise it's disabled.
+         * @hide
+         */
+        public static final String NATIVE_FLAGS_HEALTH_CHECK_ENABLED =
+                "native_flags_health_check_enabled";
+
     }
 
     /**
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
index 5ca9d15..23d607d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/service/intelligence/ContentCaptureEventsRequest.aidl
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -15,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.service.intelligence;
+
+parcelable ContentCaptureEventsRequest;
diff --git a/core/java/android/service/intelligence/ContentCaptureEventsRequest.java b/core/java/android/service/intelligence/ContentCaptureEventsRequest.java
new file mode 100644
index 0000000..bc5b92b
--- /dev/null
+++ b/core/java/android/service/intelligence/ContentCaptureEventsRequest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+/**
+ * Batch of content capture events.
+ *
+ * @hide
+ */
+@SystemApi
+public final class ContentCaptureEventsRequest implements Parcelable {
+
+    private final List<ContentCaptureEvent> mEvents;
+
+    /** @hide */
+    public ContentCaptureEventsRequest(@NonNull List<ContentCaptureEvent> events) {
+        mEvents = events;
+    }
+
+    /**
+     * Gets the events.
+     */
+    @NonNull
+    public List<ContentCaptureEvent> getEvents() {
+        return mEvents;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedList(mEvents, flags);
+    }
+
+    public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR =
+            new Parcelable.Creator<ContentCaptureEventsRequest>() {
+
+        @Override
+        public ContentCaptureEventsRequest createFromParcel(Parcel parcel) {
+            return new ContentCaptureEventsRequest(parcel
+                    .createTypedArrayList(ContentCaptureEvent.CREATOR));
+        }
+
+        @Override
+        public ContentCaptureEventsRequest[] newArray(int size) {
+            return new ContentCaptureEventsRequest[size];
+        }
+    };
+}
diff --git a/core/java/android/service/intelligence/FillController.java b/core/java/android/service/intelligence/FillController.java
index c5e1242..4a9c85d 100644
--- a/core/java/android/service/intelligence/FillController.java
+++ b/core/java/android/service/intelligence/FillController.java
@@ -15,12 +15,12 @@
  */
 package android.service.intelligence;
 
-import static android.service.intelligence.IntelligenceService.DEBUG;
+import static android.service.intelligence.SmartSuggestionsService.DEBUG;
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.RemoteException;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 import android.util.Log;
 import android.util.Pair;
 import android.view.autofill.AutofillId;
diff --git a/core/java/android/service/intelligence/FillRequest.java b/core/java/android/service/intelligence/FillRequest.java
index 95e9224..f68db9d 100644
--- a/core/java/android/service/intelligence/FillRequest.java
+++ b/core/java/android/service/intelligence/FillRequest.java
@@ -18,7 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 import android.view.autofill.AutofillId;
 
 /**
diff --git a/core/java/android/service/intelligence/FillWindow.java b/core/java/android/service/intelligence/FillWindow.java
index 4ea07bf..309f6a1 100644
--- a/core/java/android/service/intelligence/FillWindow.java
+++ b/core/java/android/service/intelligence/FillWindow.java
@@ -15,7 +15,7 @@
  */
 package android.service.intelligence;
 
-import static android.service.intelligence.IntelligenceService.DEBUG;
+import static android.service.intelligence.SmartSuggestionsService.DEBUG;
 
 import android.annotation.LongDef;
 import android.annotation.NonNull;
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index e2260d7..d6b3107 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -17,6 +17,7 @@
 package android.service.intelligence;
 
 import android.os.IBinder;
+import android.service.intelligence.ContentCaptureEventsRequest;
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.SnapshotData;
@@ -26,19 +27,19 @@
 
 import java.util.List;
 
-
 /**
  * Interface from the system to an intelligence service.
  *
  * @hide
  */
+ // TODO(b/111276913): rename / update javadoc (once the final name is defined)
 oneway interface IIntelligenceService {
 
     // Called when session is created (context not null) or destroyed (context null)
     void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
 
-    void onContentCaptureEvents(in InteractionSessionId sessionId,
-                                in List<ContentCaptureEvent> events);
+    void onContentCaptureEventsRequest(in InteractionSessionId sessionId,
+                                in ContentCaptureEventsRequest request);
 
     void onActivitySnapshot(in InteractionSessionId sessionId,
                             in SnapshotData snapshotData);
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java
index 0cc377b..7f4283d 100644
--- a/core/java/android/service/intelligence/InteractionContext.java
+++ b/core/java/android/service/intelligence/InteractionContext.java
@@ -37,7 +37,7 @@
     /**
      * Flag used to indicate that the app explicitly disabled content capture for the activity
      * (using
-     * {@link android.view.intelligence.IntelligenceManager#setContentCaptureEnabled()}),
+     * {@link android.view.intelligence.ContentCaptureManager#setContentCaptureEnabled()}),
      * in which case the service will just receive activity-level events.
      */
     public static final int FLAG_DISABLED_BY_APP = 0x1;
diff --git a/core/java/android/service/intelligence/PresentationParams.java b/core/java/android/service/intelligence/PresentationParams.java
index c59069b..9530309 100644
--- a/core/java/android/service/intelligence/PresentationParams.java
+++ b/core/java/android/service/intelligence/PresentationParams.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.graphics.Rect;
-import android.service.intelligence.IntelligenceService.AutofillProxy;
+import android.service.intelligence.SmartSuggestionsService.AutofillProxy;
 import android.util.DebugUtils;
 import android.view.View;
 
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/SmartSuggestionsService.java
similarity index 71%
rename from core/java/android/service/intelligence/IntelligenceService.java
rename to core/java/android/service/intelligence/SmartSuggestionsService.java
index 040e25e..0e29e70 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/SmartSuggestionsService.java
@@ -19,8 +19,10 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.CancellationSignal;
@@ -43,31 +45,31 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
- * A service used to capture the content of the screen.
- *
- * <p>The data collected by this service can be analyzed and combined with other sources to provide
- * contextual data in other areas of the system such as Autofill.
+ * A service used to capture the content of the screen to provide contextual data in other areas of
+ * the system such as Autofill.
  *
  * @hide
  */
 @SystemApi
-public abstract class IntelligenceService extends Service {
+public abstract class SmartSuggestionsService extends Service {
 
-    private static final String TAG = "IntelligenceService";
+    private static final String TAG = "SmartSuggestionsService";
 
     // TODO(b/111330312): STOPSHIP use dynamic value, or change to false
     static final boolean DEBUG = true;
+    static final boolean VERBOSE = false;
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
-     * {@link android.Manifest.permission#BIND_INTELLIGENCE_SERVICE} permission so
+     * {@link android.Manifest.permission#BIND_SMART_SUGGESTIONS_SERVICE} permission so
      * that other applications can not abuse it.
      */
     public static final String SERVICE_INTERFACE =
-            "android.service.intelligence.IntelligenceService";
+            "android.service.intelligence.SmartSuggestionsService";
 
     private Handler mHandler;
 
@@ -80,21 +82,21 @@
                 throws RemoteException {
             if (context != null) {
                 mHandler.sendMessage(
-                        obtainMessage(IntelligenceService::onCreateInteractionSession,
-                                IntelligenceService.this, context, sessionId));
+                        obtainMessage(SmartSuggestionsService::onCreateInteractionSession,
+                                SmartSuggestionsService.this, context, sessionId));
             } else {
                 mHandler.sendMessage(
-                        obtainMessage(IntelligenceService::onDestroyInteractionSession,
-                                IntelligenceService.this, sessionId));
+                        obtainMessage(SmartSuggestionsService::onDestroyInteractionSession,
+                                SmartSuggestionsService.this, sessionId));
             }
         }
 
         @Override
-        public void onContentCaptureEvents(InteractionSessionId sessionId,
-                List<ContentCaptureEvent> events) {
+        public void onContentCaptureEventsRequest(InteractionSessionId sessionId,
+                ContentCaptureEventsRequest request) {
             mHandler.sendMessage(
-                    obtainMessage(IntelligenceService::onContentCaptureEvent,
-                            IntelligenceService.this, sessionId, events));
+                    obtainMessage(SmartSuggestionsService::onContentCaptureEventsRequest,
+                            SmartSuggestionsService.this, sessionId, request));
 
         }
 
@@ -102,22 +104,22 @@
         public void onActivitySnapshot(InteractionSessionId sessionId,
                 SnapshotData snapshotData) {
             mHandler.sendMessage(
-                    obtainMessage(IntelligenceService::onActivitySnapshot,
-                            IntelligenceService.this, sessionId, snapshotData));
+                    obtainMessage(SmartSuggestionsService::onActivitySnapshot,
+                            SmartSuggestionsService.this, sessionId, snapshotData));
         }
 
         @Override
         public void onAutofillRequest(InteractionSessionId sessionId, IBinder client,
                 int autofilSessionId, AutofillId focusedId) {
-            mHandler.sendMessage(obtainMessage(IntelligenceService::handleOnAutofillRequest,
-                    IntelligenceService.this, sessionId, client, autofilSessionId, focusedId));
+            mHandler.sendMessage(obtainMessage(SmartSuggestionsService::handleOnAutofillRequest,
+                    SmartSuggestionsService.this, sessionId, client, autofilSessionId, focusedId));
         }
 
         @Override
         public void onDestroyAutofillWindowsRequest(InteractionSessionId sessionId) {
             mHandler.sendMessage(
-                    obtainMessage(IntelligenceService::handleOnDestroyAutofillWindowsRequest,
-                            IntelligenceService.this, sessionId));
+                    obtainMessage(SmartSuggestionsService::handleOnDestroyAutofillWindowsRequest,
+                            SmartSuggestionsService.this, sessionId));
         }
     };
 
@@ -139,25 +141,92 @@
     }
 
     /**
+     * Explicitly limits content capture to the given packages and activities.
+     *
+     * <p>When the whitelist is set, it overrides the values passed to
+     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
+     * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
+     *
+     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+     *
+     * <p>Useful when the service wants to restrict content capture to a category of apps, like
+     * chat apps. For example, if the service wants to support view captures on all activities of
+     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+     * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
+     * Arrays.asList(new ComponentName("ChatApp2", "act1"),
+     * new ComponentName("ChatApp2", "act2")));}
+     */
+    public final void setContentCaptureWhitelist(@Nullable List<String> packages,
+            @Nullable List<ComponentName> activities) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Defines whether content capture should be enabled for activities with such
+     * {@link android.content.ComponentName}.
+     *
+     * <p>Useful to blacklist a particular activity.
+     */
+    public final void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
+            boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Defines whether content capture should be enabled for activities of the app with such
+     * {@code packageName}.
+     *
+     * <p>Useful to blacklist any activity from a particular app.
+     */
+    public final void setPackageContentCaptureEnabled(@NonNull String packageName,
+            boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /**
+     * Gets the activities where content capture was disabled by
+     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
+     */
+    @NonNull
+    public final Set<ComponentName> getContentCaptureDisabledActivities() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Gets the apps where content capture was disabled by
+     * {@link #setPackageContentCaptureEnabled(String, boolean)}.
+     */
+    @NonNull
+    public final Set<String> getContentCaptureDisabledPackages() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
      * Creates a new interaction session.
      *
      * @param context interaction context
      * @param sessionId the session's Id
      */
     public void onCreateInteractionSession(@NonNull InteractionContext context,
-            @NonNull InteractionSessionId sessionId) {}
+            @NonNull InteractionSessionId sessionId) {
+        if (VERBOSE) {
+            Log.v(TAG, "onCreateInteractionSession(id=" + sessionId + ", ctx=" + context + ")");
+        }
+    }
 
     /**
      * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture
      * session.
      *
      * @param sessionId the session's Id
-     * @param events the events
+     * @param request the events
      */
     // TODO(b/111276913): rename to onContentCaptureEvents or something like that; also, pass a
     // Request object so it can be extended
-    public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
-            @NonNull List<ContentCaptureEvent> events);
+    public abstract void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+            @NonNull ContentCaptureEventsRequest request);
 
     private void handleOnAutofillRequest(@NonNull InteractionSessionId sessionId,
             @NonNull IBinder client, int autofillSessionId, @NonNull AutofillId focusedId) {
@@ -242,8 +311,7 @@
     }
 
     /**
-     * Notifies the service of {@link IntelligenceSnapshotData snapshot data} associated with a
-     * session.
+     * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
      *
      * @param sessionId the session's Id
      * @param snapshotData the data
@@ -256,7 +324,11 @@
      *
      * @param sessionId the id of the session to destroy
      */
-    public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {}
+    public void onDestroyInteractionSession(@NonNull InteractionSessionId sessionId) {
+        if (VERBOSE) {
+            Log.v(TAG, "onDestroyInteractionSession(id=" + sessionId + ")");
+        }
+    }
 
     /** @hide */
     static final class AutofillProxy {
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index ab94f43..1ddc099e 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -16,6 +16,7 @@
 
 package android.service.notification;
 
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.content.pm.ParceledListSlice;
@@ -50,4 +51,5 @@
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
     void onNotificationDirectReply(String key);
     void onSuggestedReplySent(String key, in CharSequence reply, int source);
+    void onActionClicked(String key, in Notification.Action action, int source);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 68da83f..c850a4e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -19,9 +19,11 @@
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -189,11 +191,20 @@
      * Implement this to know when a suggested reply is sent.
      * @param key the notification key
      * @param reply the reply that is just sent
-     * @param source the source of the reply, e.g. SOURCE_FROM_APP
+     * @param source the source that provided the reply, e.g. SOURCE_FROM_APP
      */
     public void onSuggestedReplySent(String key, CharSequence reply, @Source int source) {}
 
     /**
+     * Implement this to know when an action is clicked.
+     * @param key the notification key
+     * @param action the action that is just clicked
+     * @param source the source that provided the action, e.g. SOURCE_FROM_APP
+     */
+    public void onActionClicked(String key, @Nullable Notification.Action action, int source) {
+    }
+
+    /**
      * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
@@ -317,6 +328,15 @@
             args.argi2 = source;
             mHandler.obtainMessage(MyHandler.MSG_ON_SUGGESTED_REPLY_SENT, args).sendToTarget();
         }
+
+        @Override
+        public void onActionClicked(String key, Notification.Action action, int source) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = key;
+            args.arg2 = action;
+            args.argi2 = source;
+            mHandler.obtainMessage(MyHandler.MSG_ON_ACTION_CLICKED, args).sendToTarget();
+        }
     }
 
     private final class MyHandler extends Handler {
@@ -326,6 +346,7 @@
         public static final int MSG_ON_NOTIFICATION_EXPANSION_CHANGED = 4;
         public static final int MSG_ON_NOTIFICATION_DIRECT_REPLY_SENT = 5;
         public static final int MSG_ON_SUGGESTED_REPLY_SENT = 6;
+        public static final int MSG_ON_ACTION_CLICKED = 7;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -395,6 +416,15 @@
                     onSuggestedReplySent(key, reply, source);
                     break;
                 }
+                case MSG_ON_ACTION_CLICKED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    String key = (String) args.arg1;
+                    Notification.Action action = (Notification.Action) args.arg2;
+                    int source = args.argi2;
+                    args.recycle();
+                    onActionClicked(key, action, source);
+                    break;
+                }
             }
         }
     }
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 756a7c6..1fe97b7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1382,6 +1382,11 @@
         }
 
         @Override
+        public void onActionClicked(String key, Notification.Action action, int source) {
+            // no-op in the listener
+        }
+
+        @Override
         public void onNotificationChannelModification(String pkgName, UserHandle user,
                 NotificationChannel channel,
                 @ChannelOrGroupModificationTypes int modificationType) {
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 8371c31b..0e2ae83 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -806,7 +806,7 @@
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
-            messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
+            callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders());
         }
 
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 43ab8dc..194147c 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -859,6 +859,27 @@
     /**
      * @hide
      */
+    public boolean areValuesSet() {
+        return getPriorityCategoryReminders() != STATE_UNSET
+                || getPriorityCategoryEvents() != STATE_UNSET
+                || getPriorityCategoryMessages() != STATE_UNSET
+                || getPriorityCategoryCalls() != STATE_UNSET
+                || getPriorityCategoryRepeatCallers() != STATE_UNSET
+                || getPriorityCategoryAlarms() != STATE_UNSET
+                || getPriorityCategoryMedia() != STATE_UNSET
+                || getPriorityCategorySystem() != STATE_UNSET
+                || getVisualEffectFullScreenIntent() != STATE_UNSET
+                || getVisualEffectLights() != STATE_UNSET
+                || getVisualEffectPeek() != STATE_UNSET
+                || getVisualEffectStatusBar() != STATE_UNSET
+                || getVisualEffectBadge() != STATE_UNSET
+                || getVisualEffectAmbient() != STATE_UNSET
+                || getVisualEffectNotificationList() != STATE_UNSET;
+    }
+
+    /**
+     * @hide
+     */
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
 
diff --git a/core/java/android/service/oemlock/IOemLockService.aidl b/core/java/android/service/oemlock/IOemLockService.aidl
index d5e10d6..99cffc5 100644
--- a/core/java/android/service/oemlock/IOemLockService.aidl
+++ b/core/java/android/service/oemlock/IOemLockService.aidl
@@ -22,6 +22,8 @@
  * @hide
  */
 interface IOemLockService {
+    String getLockName();
+
     void setOemUnlockAllowedByCarrier(boolean allowed, in byte[] signature);
     boolean isOemUnlockAllowedByCarrier();
 
diff --git a/core/java/android/service/oemlock/OemLockManager.java b/core/java/android/service/oemlock/OemLockManager.java
index f0d6603..029d645 100644
--- a/core/java/android/service/oemlock/OemLockManager.java
+++ b/core/java/android/service/oemlock/OemLockManager.java
@@ -44,6 +44,23 @@
     }
 
     /**
+     * Returns a vendor specific name for the OEM lock.
+     *
+     * This value is used to identify the security protocol used by locks.
+     *
+     * @return The name of the OEM lock or {@code null} if failed to get the name.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE)
+    @Nullable
+    public String getLockName() {
+        try {
+            return mService.getLockName();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Sets whether the carrier has allowed this device to be OEM unlocked.
      *
      * Depending on the implementation, the validity of the request might need to be proved. This
diff --git a/core/java/android/service/sms/FinancialSmsService.java b/core/java/android/service/sms/FinancialSmsService.java
new file mode 100644
index 0000000..5fb7249
--- /dev/null
+++ b/core/java/android/service/sms/FinancialSmsService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.sms;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.database.CursorWindow;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+/**
+ * A service to support sms messages read for financial apps.
+ *
+ * {@hide}
+ */
+@SystemApi
+public abstract class FinancialSmsService extends Service {
+
+    private static final String TAG = "FinancialSmsService";
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service
+     * in its manifest for the system to recognize it as a quota providing
+     * service.
+     */
+    public static final String ACTION_FINANCIAL_SERVICE_INTENT =
+            "android.service.sms.action.FINANCIAL_SERVICE_INTENT";
+
+    /** {@hide} **/
+    public static final String EXTRA_SMS_MSGS = "sms_messages";
+
+    private FinancialSmsServiceWrapper mWrapper;
+
+    private void getSmsMessages(RemoteCallback callback, Bundle params) {
+        final Bundle data = new Bundle();
+        CursorWindow smsMessages = onGetSmsMessages(params);
+        if (smsMessages != null) {
+            data.putParcelable(EXTRA_SMS_MSGS, smsMessages);
+        }
+        callback.sendResult(data);
+    }
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    /** @hide */
+    public FinancialSmsService() {
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWrapper = new FinancialSmsServiceWrapper();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Get sms messages for financial apps.
+     *
+     * @param params parameters passed in by the calling app.
+     * @return the {@code CursorWindow} with all sms messages for the app to read.
+     *
+     * {@hide}
+     */
+    @Nullable
+    @SystemApi
+    public abstract CursorWindow onGetSmsMessages(@NonNull Bundle params);
+
+    private final class FinancialSmsServiceWrapper extends IFinancialSmsService.Stub {
+        @Override
+        public void getSmsMessages(RemoteCallback callback, Bundle params) throws RemoteException {
+            mHandler.sendMessage(obtainMessage(
+                    FinancialSmsService::getSmsMessages,
+                    FinancialSmsService.this,
+                    callback, params));
+        }
+    }
+
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/service/sms/IFinancialSmsService.aidl
similarity index 70%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/android/service/sms/IFinancialSmsService.aidl
index 27d25b8..caabe58 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/android/service/sms/IFinancialSmsService.aidl
@@ -13,12 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package android.service.sms;
+
+import android.os.Bundle;
+import android.os.RemoteCallback;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * Service used by financial apps to read sms messages.
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
-}
+oneway interface IFinancialSmsService
+{
+    void getSmsMessages(in RemoteCallback callback, in Bundle params);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index dccce40..ebce484 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -27,7 +27,7 @@
     void setDesiredSize(int width, int height);
     void setDisplayPadding(in Rect padding);
     void setVisibility(boolean visible);
-    void setInAmbientMode(boolean inAmbientDisplay, boolean animated);
+    void setInAmbientMode(boolean inAmbientDisplay, long animationDuration);
     void dispatchPointer(in MotionEvent event);
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras);
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f6bb762..a095b0d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
 import android.app.WallpaperColors;
@@ -56,6 +57,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -184,6 +186,7 @@
         final DisplayCutout.ParcelableWrapper mDisplayCutout =
                 new DisplayCutout.ParcelableWrapper();
         DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
+        final InsetsState mInsetsState = new InsetsState();
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -440,7 +443,9 @@
         /**
          * Returns true if this engine is running in ambient mode -- that is,
          * it is being shown in low power mode, on always on display.
+         * @hide
          */
+        @SystemApi
         public boolean isInAmbientMode() {
             return mIsInAmbientMode;
         }
@@ -566,14 +571,16 @@
          * Called when the device enters or exits ambient mode.
          *
          * @param inAmbientMode {@code true} if in ambient mode.
-         * @param animated {@code true} if you'll have the opportunity of animating your transition
-         *                 {@code false} when the wallpaper should present its ambient version
-         *                 immediately.
+         * @param animationDuration How long the transition animation to change the ambient state
+         *                          should run, in milliseconds. If 0 is passed as the argument
+         *                          here, the state should be switched immediately.
          *
          * @see #isInAmbientMode()
          * @see WallpaperInfo#supportsAmbientMode()
+         * @hide
          */
-        public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+        @SystemApi
+        public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
         }
 
         /**
@@ -803,9 +810,11 @@
                         mLayout.windowAnimations =
                                 com.android.internal.R.style.Animation_Wallpaper;
                         mInputChannel = new InputChannel();
+
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                                 mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets,
-                                mOutsets, mDisplayCutout, mInputChannel) < 0) {
+                                mOutsets, mDisplayCutout, mInputChannel,
+                                mInsetsState) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -831,7 +840,8 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+                            mInsetsState);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -1044,19 +1054,19 @@
          * message sent from handler.
          *
          * @param inAmbientMode {@code true} if in ambient mode.
-         * @param animated {@code true} if the transition will be animated.
+         * @param animationDuration For how long the transition will last, in ms.
          * @hide
          */
         @VisibleForTesting
-        public void doAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+        public void doAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
             if (!mDestroyed) {
                 if (DEBUG) {
                     Log.v(TAG, "onAmbientModeChanged(" + inAmbientMode + ", "
-                            + animated + "): " + this);
+                            + animationDuration + "): " + this);
                 }
                 mIsInAmbientMode = inAmbientMode;
                 if (mCreated) {
-                    onAmbientModeChanged(inAmbientMode, animated);
+                    onAmbientModeChanged(inAmbientMode, animationDuration);
                 }
             }
         }
@@ -1315,10 +1325,10 @@
         }
 
         @Override
-        public void setInAmbientMode(boolean inAmbientDisplay, boolean animated)
+        public void setInAmbientMode(boolean inAmbientDisplay, long animationDuration)
                 throws RemoteException {
-            Message msg = mCaller.obtainMessageII(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
-                    animated ? 1 : 0);
+            Message msg = mCaller.obtainMessageIO(DO_IN_AMBIENT_MODE, inAmbientDisplay ? 1 : 0,
+                    animationDuration);
             mCaller.sendMessage(msg);
         }
 
@@ -1389,7 +1399,7 @@
                     return;
                 }
                 case DO_IN_AMBIENT_MODE: {
-                    mEngine.doAmbientModeChanged(message.arg1 != 0, message.arg2 != 0);
+                    mEngine.doAmbientModeChanged(message.arg1 != 0, (Long) message.obj);
                     return;
                 }
                 case MSG_UPDATE_SURFACE:
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2cf0262..8cb18b2 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -485,7 +485,7 @@
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521430)
     public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
                         Alignment align, TextDirectionHeuristic textDir,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index b970c25..85b6b88 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -46,6 +46,7 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LineBackgroundSpan;
+import android.text.style.LineHeightSpan;
 import android.text.style.LocaleSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
@@ -733,7 +734,9 @@
     /** @hide */
     public static final int LINE_BACKGROUND_SPAN = 27;
     /** @hide */
-    public static final int LAST_SPAN = LINE_BACKGROUND_SPAN;
+    public static final int LINE_HEIGHT_SPAN = 28;
+    /** @hide */
+    public static final int LAST_SPAN = LINE_HEIGHT_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -928,6 +931,10 @@
                     readSpan(p, sp, new LineBackgroundSpan.Standard(p));
                     break;
 
+                case LINE_HEIGHT_SPAN:
+                    readSpan(p, sp, new LineHeightSpan.Standard(p));
+                    break;
+                    
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 562ae7a..bab4bc3 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -18,8 +18,8 @@
 
 import android.util.TimeFormatException;
 
+import libcore.timezone.ZoneInfoDB;
 import libcore.util.ZoneInfo;
-import libcore.util.ZoneInfoDB;
 
 import java.io.IOException;
 import java.util.Locale;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 2742ae0..a5d5af2 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -20,7 +20,10 @@
 import android.annotation.NonNull;
 import android.annotation.Px;
 import android.graphics.Paint;
+import android.os.Parcel;
+import android.text.ParcelableSpan;
 import android.text.TextPaint;
+import android.text.TextUtils;
 
 import com.android.internal.util.Preconditions;
 
@@ -71,7 +74,7 @@
      * covers only part of the paragraph.
      * </p>
      */
-    class Standard implements LineHeightSpan {
+    class Standard implements LineHeightSpan, ParcelableSpan {
 
         private final @Px int mHeight;
         /**
@@ -82,6 +85,48 @@
             mHeight = height;
         }
 
+        /**
+         * Constructor called from {@link TextUtils} to restore the span from a parcel
+         */
+        public Standard(Parcel src) {
+            mHeight = src.readInt();
+        }
+
+        /**
+         * Returns the line height specified by this span.
+         */
+        @Px
+        public int getHeight() {
+            return mHeight;
+        }
+
+        @Override
+        public int getSpanTypeId() {
+            return getSpanTypeIdInternal();
+        }
+
+        /** @hide */
+        @Override
+        public int getSpanTypeIdInternal() {
+            return TextUtils.LINE_HEIGHT_SPAN;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            writeToParcelInternal(dest, flags);
+        }
+
+        /** @hide */
+        @Override
+        public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
+            dest.writeInt(mHeight);
+        }
+
         @Override
         public void chooseHeight(@NonNull CharSequence text, int start, int end,
                 int spanstartv, int lineHeight,
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index be47320..433483f7 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -369,10 +369,7 @@
 
     /**
      * @return The color of the underline for that span, or 0 if there is no underline
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
     public int getUnderlineColor() {
         // The order here should match what is used in updateDrawState
         final boolean misspelled = (mFlags & FLAG_MISSPELLED) != 0;
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index c822832..de182da 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -32,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.View;
@@ -109,7 +110,7 @@
                 }
             };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final Property<View, PointF> BOTTOM_RIGHT_ONLY_PROPERTY =
             new Property<View, PointF>(PointF.class, "bottomRight") {
                 @Override
@@ -144,7 +145,7 @@
                 }
             };
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final Property<View, PointF> POSITION_PROPERTY =
             new Property<View, PointF>(PointF.class, "position") {
                 @Override
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 7e499f2..b1fc17a 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.os.Build;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -38,9 +39,9 @@
     private int mLayoutId = -1;
     private ViewGroup mSceneRoot;
     private View mLayout; // alternative to layoutId
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Runnable mEnterAction;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     Runnable mExitAction;
 
     /**
@@ -200,7 +201,7 @@
      *
      * @param sceneRoot The view on which the current scene is being set
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     static void setCurrentScene(@NonNull View sceneRoot, @Nullable Scene scene) {
         sceneRoot.setTagInternal(com.android.internal.R.id.current_scene, scene);
     }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5f348c4..dee6d90 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,7 +45,6 @@
         DEFAULT_FLAGS.put("settings_systemui_theme", "true");
         DEFAULT_FLAGS.put("settings_dynamic_homepage", "true");
         DEFAULT_FLAGS.put("settings_mobile_network_v2", "true");
-        DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
         DEFAULT_FLAGS.put("settings_seamless_transfer", "false");
         DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
         DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false");
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 21c4252..57d55bf 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -24,7 +24,7 @@
 import libcore.timezone.CountryTimeZones;
 import libcore.timezone.CountryTimeZones.TimeZoneMapping;
 import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 3ee5f1f..33b3ff4f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -18,6 +18,7 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 
@@ -225,7 +226,7 @@
     private int mMinimumFlingVelocity;
     private int mMaximumFlingVelocity;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
     private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
     private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
@@ -592,8 +593,8 @@
 
                 if (mIsLongpressEnabled) {
                     mHandler.removeMessages(LONG_PRESS);
-                    mHandler.sendEmptyMessageAtTime(LONG_PRESS,
-                            mCurrentDownEvent.getDownTime() + LONGPRESS_TIMEOUT);
+                    mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+                            + ViewConfiguration.getLongPressTimeout());
                 }
                 mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
                         mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b8b7f3..5e6d3d1 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -24,6 +24,8 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.DisplayCutout;
+import android.view.InsetsState;
+import android.view.InsetsSourceControl;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -53,6 +55,17 @@
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
             boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
             in DisplayCutout.ParcelableWrapper displayCutout);
+
+    /**
+     * Called when the window insets configuration has changed.
+     */
+    void insetsChanged(in InsetsState insetsState);
+
+    /**
+     * Called when this window retrieved control over a specified set of inset sources.
+     */
+    void insetsControlChanged(in InsetsState insetsState, in InsetsSourceControl[] activeControls);
+
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 308a000..c4be0e5 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -96,8 +96,9 @@
      */
     void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback,
-            boolean scaleUp);
-    void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter);
+            boolean scaleUp, int displayId);
+    void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter,
+            int displayId);
     void executeAppTransition();
 
     /**
@@ -294,14 +295,16 @@
     void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled);
 
     /**
-     * Device has a software navigation bar (separate from the status bar).
+     * Device has a software navigation bar (separate from the status bar) on specific display.
+     *
+     * @param displayId the id of display to check if there is a software navigation bar.
      */
-    boolean hasNavigationBar();
+    boolean hasNavigationBar(int displayId);
 
     /**
      * Get the position of the nav bar
      */
-    int getNavBarPosition();
+    int getNavBarPosition(int displayId);
 
     /**
      * Lock the device immediately with the specified options (can be null).
@@ -548,4 +551,16 @@
      * @see KeyguardManager#isDeviceLocked()
      */
     void setShouldShowIme(int displayId, boolean shouldShow);
+
+     /**
+     * Reparent the top layers for a display to the requested surfaceControl. The display that
+     * is going to be re-parented (the displayId passed in) needs to have been created by the same
+     * process that is requesting the re-parent. This is to ensure clients can't just re-parent
+     * display content info to any SurfaceControl, as this would be a security issue.
+     *
+     * @param displayId The id of the display.
+     * @param surfaceControlHandle The SurfaceControl handle that the top level layers for the
+     *        display should be re-parented to.
+     */
+    void reparentDisplayContent(int displayId, in IBinder surfaceControlHandle);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index bedfa9f..9762586 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -28,6 +28,7 @@
 import android.view.IWindowId;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -40,10 +41,11 @@
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outFrame,
             out Rect outContentInsets, out Rect outStableInsets, out Rect outOutsets,
-            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+            out InsetsState insetsState);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets);
+            out Rect outStableInsets, out InsetsState insetsState);
     void remove(IWindow window);
 
     /**
@@ -86,6 +88,7 @@
      * config for window, if it is now becoming visible and the merged configuration has changed
      * since it was last displayed.
      * @param outSurface Object in which is placed the new display surface.
+     * @param insetsState The current insets state in the system.
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
      * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
@@ -96,7 +99,8 @@
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
-            out MergedConfiguration outMergedConfiguration, out Surface outSurface);
+            out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+            out InsetsState insetsState);
 
     /*
      * Notify the window manager that an application is relaunching and
diff --git a/core/java/android/view/InputApplicationHandle.java b/core/java/android/view/InputApplicationHandle.java
index dc1e505..5f6bc23 100644
--- a/core/java/android/view/InputApplicationHandle.java
+++ b/core/java/android/view/InputApplicationHandle.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.os.IBinder;
+
 /**
  * Functions as a handle for an application that can receive input.
  * Enables the native input dispatcher to refer indirectly to the window manager's
@@ -28,19 +30,18 @@
     @SuppressWarnings("unused")
     private long ptr;
 
-    // The window manager's application window token.
-    public final Object appWindowToken;
-
     // Application name.
     public String name;
 
     // Dispatching timeout.
     public long dispatchingTimeoutNanos;
 
+    public IBinder token;
+
     private native void nativeDispose();
 
-    public InputApplicationHandle(Object appWindowToken) {
-        this.appWindowToken = appWindowToken;
+    public InputApplicationHandle(IBinder token) {
+        this.token = token;
     }
 
     @Override
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 621ee89..92e0009 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Region;
+import android.os.IBinder;
 import android.view.IWindow;
 import android.view.InputChannel;
 
@@ -37,8 +38,8 @@
     // The client window.
     public final IWindow clientWindow;
 
-    // The input channel associated with the window.
-    public InputChannel inputChannel;
+    // The token assosciated with the window.
+    public IBinder token;
 
     // The window name.
     public String name;
@@ -56,6 +57,8 @@
     public int frameRight;
     public int frameBottom;
 
+    public int surfaceInset;
+
     // Global scaling factor applied to touch events when they are dispatched
     // to the window
     public float scaleFactor;
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
new file mode 100644
index 0000000..ba5340c
--- /dev/null
+++ b/core/java/android/view/InsetsController.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+
+/**
+ * Implements {@link WindowInsetsController} on the client.
+ * @hide
+ */
+public class InsetsController implements WindowInsetsController {
+
+    private final InsetsState mState = new InsetsState();
+    private final Rect mFrame = new Rect();
+    private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+
+    private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
+
+    void onFrameChanged(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public InsetsState getState() {
+        return mState;
+    }
+
+    public void setState(InsetsState state) {
+        mState.set(state);
+    }
+
+    /**
+     * @see InsetsState#calculateInsets
+     */
+    WindowInsets calculateInsets(boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        return mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout);
+    }
+
+    /**
+     * Called when the server has dispatched us a new set of inset controls.
+     */
+    public void onControlsChanged(InsetsSourceControl[] activeControls) {
+        if (activeControls != null) {
+            for (InsetsSourceControl activeControl : activeControls) {
+                mTmpControlArray.put(activeControl.getType(), activeControl);
+            }
+        }
+
+        // Ensure to update all existing source consumers
+        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            final InsetsSourceControl control = mTmpControlArray.get(consumer.getType());
+
+            // control may be null, but we still need to update the control to null if it got
+            // revoked.
+            consumer.setControl(control);
+        }
+
+        // Ensure to create source consumers if not available yet.
+        for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
+            final InsetsSourceControl control = mTmpControlArray.valueAt(i);
+            getSourceConsumer(control.getType()).setControl(control);
+        }
+        mTmpControlArray.clear();
+    }
+
+    @Override
+    public void show(@InsetType int types) {
+        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+            getSourceConsumer(internalTypes.valueAt(i)).show();
+        }
+    }
+
+    @Override
+    public void hide(@InsetType int types) {
+        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
+        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+            getSourceConsumer(internalTypes.valueAt(i)).hide();
+        }
+    }
+
+    @VisibleForTesting
+    public @NonNull InsetsSourceConsumer getSourceConsumer(@InternalInsetType int type) {
+        InsetsSourceConsumer controller = mSourceConsumers.get(type);
+        if (controller != null) {
+            return controller;
+        }
+        controller = new InsetsSourceConsumer(type, mState, Transaction::new);
+        mSourceConsumers.put(type, controller);
+        return controller;
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix); pw.println("InsetsController:");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
new file mode 100644
index 0000000..0cb8ad7
--- /dev/null
+++ b/core/java/android/view/InsetsSource.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents the state of a single window generating insets for clients.
+ * @hide
+ */
+public class InsetsSource implements Parcelable {
+
+    private final @InternalInsetType int mType;
+
+    /** Frame of the source in screen coordinate space */
+    private final Rect mFrame;
+    private boolean mVisible;
+
+    private final Rect mTmpFrame = new Rect();
+
+    public InsetsSource(@InternalInsetType int type) {
+        mType = type;
+        mFrame = new Rect();
+    }
+
+    public InsetsSource(InsetsSource other) {
+        mType = other.mType;
+        mFrame = new Rect(other.mFrame);
+        mVisible = other.mVisible;
+    }
+
+    public void setFrame(Rect frame) {
+        mFrame.set(frame);
+    }
+
+    public void setVisible(boolean visible) {
+        mVisible = visible;
+    }
+
+    public @InternalInsetType int getType() {
+        return mType;
+    }
+
+    public Rect getFrame() {
+        return mFrame;
+    }
+
+    /**
+     * Calculates the insets this source will cause to a client window.
+     *
+     * @param relativeFrame The frame to calculate the insets relative to.
+     * @param ignoreVisibility If true, always reports back insets even if source isn't visible.
+     * @return The resulting insets.
+     */
+    public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+        if (!ignoreVisibility && !mVisible) {
+            return Insets.NONE;
+        }
+        if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+            return Insets.NONE;
+        }
+
+        // Intersecting at top/bottom
+        if (mTmpFrame.width() == relativeFrame.width()) {
+            if (mTmpFrame.top == relativeFrame.top) {
+                return Insets.of(0, mTmpFrame.height(), 0, 0);
+            } else {
+                return Insets.of(0, 0, 0, mTmpFrame.height());
+            }
+        }
+        // Intersecting at left/right
+        else if (mTmpFrame.height() == relativeFrame.height()) {
+            if (mTmpFrame.left == relativeFrame.left) {
+                return Insets.of(mTmpFrame.width(), 0, 0, 0);
+            } else {
+                return Insets.of(0, 0, mTmpFrame.width(), 0);
+            }
+        } else {
+            return Insets.NONE;
+        }
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix);
+        pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
+        pw.print(" frame="); pw.print(mFrame.toShortString());
+        pw.print(" visible="); pw.print(mVisible);
+        pw.println();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        InsetsSource that = (InsetsSource) o;
+
+        if (mType != that.mType) return false;
+        if (mVisible != that.mVisible) return false;
+        return mFrame.equals(that.mFrame);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mType;
+        result = 31 * result + mFrame.hashCode();
+        result = 31 * result + (mVisible ? 1 : 0);
+        return result;
+    }
+
+    public InsetsSource(Parcel in) {
+        mType = in.readInt();
+        mFrame = in.readParcelable(null /* loader */);
+        mVisible = in.readBoolean();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeParcelable(mFrame, 0 /* flags*/);
+        dest.writeBoolean(mVisible);
+    }
+
+    public static final Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
+
+        public InsetsSource createFromParcel(Parcel in) {
+            return new InsetsSource(in);
+        }
+
+        public InsetsSource[] newArray(int size) {
+            return new InsetsSource[size];
+        }
+    };
+}
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
new file mode 100644
index 0000000..e74aa8d
--- /dev/null
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.annotation.Nullable;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsState.InternalInsetType;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Supplier;
+
+/**
+ * Controls the visibility and animations of a single window insets source.
+ * @hide
+ */
+public class InsetsSourceConsumer {
+
+    private final Supplier<Transaction> mTransactionSupplier;
+    private final @InternalInsetType int mType;
+    private final InsetsState mState;
+    private @Nullable InsetsSourceControl mControl;
+    private boolean mHidden;
+
+    public InsetsSourceConsumer(@InternalInsetType int type, InsetsState state,
+            Supplier<Transaction> transactionSupplier) {
+        mType = type;
+        mState = state;
+        mTransactionSupplier = transactionSupplier;
+    }
+
+    public void setControl(@Nullable InsetsSourceControl control) {
+        if (mControl == control) {
+            return;
+        }
+        mControl = control;
+        applyHiddenToControl();
+    }
+
+    @VisibleForTesting
+    public InsetsSourceControl getControl() {
+        return mControl;
+    }
+
+    int getType() {
+        return mType;
+    }
+
+    @VisibleForTesting
+    public void show() {
+        setHidden(false);
+    }
+
+    @VisibleForTesting
+    public void hide() {
+        setHidden(true);
+    }
+
+    private void setHidden(boolean hidden) {
+        if (mHidden == hidden) {
+            return;
+        }
+        mHidden = hidden;
+        applyHiddenToControl();
+    }
+
+    private void applyHiddenToControl() {
+        if (mControl == null) {
+            return;
+        }
+
+        // TODO: Animation
+        final Transaction t = mTransactionSupplier.get();
+        if (mHidden) {
+            t.hide(mControl.getLeash());
+        } else {
+            t.show(mControl.getLeash());
+        }
+        t.apply();
+    }
+}
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/view/InsetsSourceControl.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/view/InsetsSourceControl.aidl
index 5ca9d15..755bf45 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/view/InsetsSourceControl.aidl
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /**
  * Copyright (c) 2018, The Android Open Source Project
  *
@@ -15,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.view;
+
+parcelable InsetsSourceControl;
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
new file mode 100644
index 0000000..9383e6c
--- /dev/null
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.InsetsState.InternalInsetType;
+
+/**
+ * Represents a parcelable object to allow controlling a single {@link InsetsSource}.
+ * @hide
+ */
+public class InsetsSourceControl implements Parcelable {
+
+    private final @InternalInsetType int mType;
+    private final SurfaceControl mLeash;
+
+    public InsetsSourceControl(@InternalInsetType int type, SurfaceControl leash) {
+        mType = type;
+        mLeash = leash;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public SurfaceControl getLeash() {
+        return mLeash;
+    }
+
+    public InsetsSourceControl(Parcel in) {
+        mType = in.readInt();
+        mLeash = in.readParcelable(null /* loader */);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeParcelable(mLeash, 0 /* flags*/);
+    }
+
+    public static final Creator<InsetsSourceControl> CREATOR
+            = new Creator<InsetsSourceControl>() {
+        public InsetsSourceControl createFromParcel(Parcel in) {
+            return new InsetsSourceControl(in);
+        }
+
+        public InsetsSourceControl[] newArray(int size) {
+            return new InsetsSourceControl[size];
+        }
+    };
+}
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/core/java/android/view/InsetsState.aidl
similarity index 63%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to core/java/android/view/InsetsState.aidl
index 5ca9d15..d02ddd1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/core/java/android/view/InsetsState.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
 /**
- * Copyright (c) 2018, The Android Open Source Project
+ * 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.
@@ -15,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.view;
+
+parcelable InsetsState;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
new file mode 100644
index 0000000..689b14f
--- /dev/null
+++ b/core/java/android/view/InsetsState.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.annotation.IntDef;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsets.Type.InsetType;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Holder for state of system windows that cause window insets for all other windows in the system.
+ * @hide
+ */
+public class InsetsState implements Parcelable {
+
+    /**
+     * Internal representation of inset source types. This is different from the public API in
+     * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows
+     * at the same time.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "TYPE", value = {
+            TYPE_TOP_BAR,
+            TYPE_SIDE_BAR_1,
+            TYPE_SIDE_BAR_2,
+            TYPE_SIDE_BAR_3,
+            TYPE_IME
+    })
+    public @interface InternalInsetType {}
+
+    static final int FIRST_TYPE = 0;
+
+    /** Top bar. Can be status bar or caption in freeform windowing mode. */
+    public static final int TYPE_TOP_BAR = FIRST_TYPE;
+
+    /**
+     * Up to 3 side bars that appear on left/right/bottom. On phones there is only one side bar
+     * (the navigation bar, see {@link #TYPE_NAVIGATION_BAR}), but other form factors might have
+     * multiple, like Android Auto.
+     */
+    public static final int TYPE_SIDE_BAR_1 = 1;
+    public static final int TYPE_SIDE_BAR_2 = 2;
+    public static final int TYPE_SIDE_BAR_3 = 3;
+
+    /** Input method window. */
+    public static final int TYPE_IME = 4;
+    static final int LAST_TYPE = TYPE_IME;
+
+    // Derived types
+
+    /** First side bar is navigation bar. */
+    public static final int TYPE_NAVIGATION_BAR = TYPE_SIDE_BAR_1;
+
+    /** A shelf is the same as the navigation bar. */
+    public static final int TYPE_SHELF = TYPE_NAVIGATION_BAR;
+
+    private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+
+    public InsetsState() {
+    }
+
+    /**
+     * Calculates {@link WindowInsets} based on the current source configuration.
+     *
+     * @param frame The frame to calculate the insets relative to.
+     * @return The calculated insets.
+     */
+    public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
+            boolean alwaysConsumeNavBar, DisplayCutout cutout) {
+        Insets systemInsets = Insets.NONE;
+        Insets maxInsets = Insets.NONE;
+        final Rect relativeFrame = new Rect(frame);
+        final Rect relativeFrameMax = new Rect(frame);
+        for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+            InsetsSource source = mSources.get(type);
+            if (source == null) {
+                continue;
+            }
+            systemInsets = processSource(source, systemInsets, relativeFrame,
+                    false /* ignoreVisibility */);
+
+            // IME won't be reported in max insets as the size depends on the EditorInfo of the IME
+            // target.
+            if (source.getType() != TYPE_IME) {
+                maxInsets = processSource(source, maxInsets, relativeFrameMax,
+                        true /* ignoreVisibility */);
+            }
+        }
+        return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+                alwaysConsumeNavBar, cutout);
+    }
+
+    private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
+            boolean ignoreVisibility) {
+        Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
+        insets = Insets.add(currentInsets, insets);
+        relativeFrame.inset(insets);
+        return insets;
+    }
+
+    public InsetsSource getSource(@InternalInsetType int type) {
+        return mSources.computeIfAbsent(type, InsetsSource::new);
+    }
+
+    /**
+     * Modifies the state of this class to exclude a certain type to make it ready for dispatching
+     * to the client.
+     *
+     * @param type The {@link InternalInsetType} of the source to remove
+     */
+    public void removeSource(int type) {
+        mSources.remove(type);
+    }
+
+    public void set(InsetsState other) {
+        set(other, false /* copySources */);
+    }
+
+    public void set(InsetsState other, boolean copySources) {
+        mSources.clear();
+        if (copySources) {
+            for (int i = 0; i < other.mSources.size(); i++) {
+                InsetsSource source = other.mSources.valueAt(i);
+                mSources.put(source.getType(), new InsetsSource(source));
+            }
+        } else {
+            mSources.putAll(other.mSources);
+        }
+    }
+
+    public static @InternalInsetType ArraySet<Integer> toInternalType(@InsetType int insetTypes) {
+        final ArraySet<Integer> result = new ArraySet<>();
+        if ((insetTypes & Type.TOP_BAR) != 0) {
+            result.add(TYPE_TOP_BAR);
+        }
+        if ((insetTypes & Type.SIDE_BARS) != 0) {
+            result.add(TYPE_SIDE_BAR_1);
+            result.add(TYPE_SIDE_BAR_2);
+            result.add(TYPE_SIDE_BAR_3);
+        }
+        if ((insetTypes & Type.IME) != 0) {
+            result.add(TYPE_IME);
+        }
+        return result;
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "InsetsState");
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            mSources.valueAt(i).dump(prefix + "  ", pw);
+        }
+    }
+
+    static String typeToString(int type) {
+        switch (type) {
+            case TYPE_TOP_BAR:
+                return "TYPE_TOP_BAR";
+            case TYPE_SIDE_BAR_1:
+                return "TYPE_SIDE_BAR_1";
+            case TYPE_SIDE_BAR_2:
+                return "TYPE_SIDE_BAR_2";
+            case TYPE_SIDE_BAR_3:
+                return "TYPE_SIDE_BAR_3";
+            case TYPE_IME:
+                return "TYPE_IME";
+            default:
+                return "TYPE_UNKNOWN";
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) { return true; }
+        if (o == null || getClass() != o.getClass()) { return false; }
+
+        InsetsState state = (InsetsState) o;
+
+        if (mSources.size() != state.mSources.size()) {
+            return false;
+        }
+        for (int i = mSources.size() - 1; i >= 0; i--) {
+            InsetsSource source = mSources.valueAt(i);
+            InsetsSource otherSource = state.mSources.get(source.getType());
+            if (otherSource == null) {
+                return false;
+            }
+            if (!otherSource.equals(source)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mSources.hashCode();
+    }
+
+    public InsetsState(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSources.size());
+        for (int i = 0; i < mSources.size(); i++) {
+            dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+        }
+    }
+
+    public static final Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+
+        public InsetsState createFromParcel(Parcel in) {
+            return new InsetsState(in);
+        }
+
+        public InsetsState[] newArray(int size) {
+            return new InsetsState[size];
+        }
+    };
+
+    public void readFromParcel(Parcel in) {
+        mSources.clear();
+        final int size = in.readInt();
+        for (int i = 0; i < size; i++) {
+            final InsetsSource source = in.readParcelable(null /* loader */);
+            mSources.put(source.getType(), source);
+        }
+    }
+}
+
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 0739516..9cced4e 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1803,6 +1803,28 @@
     }
 
     /**
+     * Returns whether this key will be sent to the
+     * {@link android.media.session.MediaSession.Callback} if not handled.
+     */
+    public static final boolean isMediaSessionKey(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                return true;
+        }
+        return false;
+    }
+
+    /**
      * Returns true if the specified keycode is a gamepad button.
      * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
      */
@@ -1861,31 +1883,6 @@
         }
     }
 
-    /**
-     * Whether this key is a media key, which can be send to apps that are
-     * interested in media key events.
-     *
-     * @hide
-     */
-    public static final boolean isMediaKey(int keyCode) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-                return true;
-        }
-        return false;
-    }
-
-
     /** Is this a system key? System keys can not be used for menu shortcuts.
      * @hide
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a7a5024..ab01085 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -104,6 +104,8 @@
             int flags, int mask);
     private static native void nativeSetWindowCrop(long transactionObj, long nativeObject,
             int l, int t, int r, int b);
+    private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
+            float cornerRadius);
     private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
             int layerStack);
 
@@ -375,9 +377,13 @@
          * Construct a new {@link SurfaceControl} with the set parameters.
          */
         public SurfaceControl build() {
-            if (mWidth <= 0 || mHeight <= 0) {
+            if (mWidth < 0 || mHeight < 0) {
                 throw new IllegalArgumentException(
-                        "width and height must be set");
+                        "width and height must be positive or unset");
+            }
+            if ((mWidth > 0 || mHeight > 0) && (isColorLayerSet() || isContainerLayerSet())) {
+                throw new IllegalArgumentException(
+                        "Only buffer layers can set a valid buffer size.");
             }
             return new SurfaceControl(mSession, mName, mWidth, mHeight, mFormat,
                     mFlags, mParent, mWindowType, mOwnerUid);
@@ -399,8 +405,8 @@
          * @param width The buffer width in pixels.
          * @param height The buffer height in pixels.
          */
-        public Builder setSize(int width, int height) {
-            if (width <= 0 || height <= 0) {
+        public Builder setBufferSize(int width, int height) {
+            if (width < 0 || height < 0) {
                 throw new IllegalArgumentException(
                         "width and height must be positive");
             }
@@ -533,6 +539,10 @@
             return this;
         }
 
+        private boolean isColorLayerSet() {
+            return  (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+        }
+
         /**
          * Indicates whether a 'ContainerLayer' is to be constructed.
          *
@@ -550,6 +560,10 @@
             return this;
         }
 
+        private boolean isContainerLayerSet() {
+            return  (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
+        }
+
         /**
          * Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}.
          *
@@ -869,10 +883,10 @@
         }
     }
 
-    public void setSize(int w, int h) {
+    public void setBufferSize(int w, int h) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
-            sGlobalTransaction.setSize(this, w, h);
+            sGlobalTransaction.setBufferSize(this, w, h);
         }
     }
 
@@ -994,6 +1008,18 @@
         }
     }
 
+    /**
+     * Sets the corner radius of a {@link SurfaceControl}.
+     *
+     * @param cornerRadius Corner radius in pixels.
+     */
+    public void setCornerRadius(float cornerRadius) {
+        checkNotReleased();
+        synchronized (SurfaceControl.class) {
+            sGlobalTransaction.setCornerRadius(this, cornerRadius);
+        }
+    }
+
     public void setLayerStack(int layerStack) {
         checkNotReleased();
         synchronized(SurfaceControl.class) {
@@ -1427,7 +1453,7 @@
         }
 
         @UnsupportedAppUsage
-        public Transaction setSize(SurfaceControl sc, int w, int h) {
+        public Transaction setBufferSize(SurfaceControl sc, int w, int h) {
             sc.checkNotReleased();
             mResizedSurfaces.put(sc, new Point(w, h));
             nativeSetSize(mNativeObject, sc.mNativeObject, w, h);
@@ -1517,6 +1543,20 @@
             return this;
         }
 
+        /**
+         * Sets the corner radius of a {@link SurfaceControl}.
+         * @param sc SurfaceControl
+         * @param cornerRadius Corner radius in pixels.
+         * @return Itself.
+         */
+        @UnsupportedAppUsage
+        public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) {
+            sc.checkNotReleased();
+            nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius);
+
+            return this;
+        }
+
         @UnsupportedAppUsage
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             sc.checkNotReleased();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2b68ec0..797d1c5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -557,7 +557,7 @@
                             name,
                             (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
                             new SurfaceControl.Builder(mSurfaceSession)
-                                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                                     .setFormat(mFormat)
                                     .setFlags(mSurfaceFlags));
                 } else if (mSurfaceControl == null) {
@@ -595,10 +595,14 @@
                             mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
                                     0.0f, 0.0f,
                                     mScreenRect.height() / (float) mSurfaceHeight);
+                            // Set a window crop when creating the surface or changing its size to
+                            // crop the buffer to the surface size since the buffer producer may
+                            // use SCALING_MODE_SCALE and submit a larger size than the surface
+                            // size.
+                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
                         }
                         if (sizeChanged && !creating) {
-                            mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
-                            mSurfaceControl.setWindowCrop(mSurfaceWidth, mSurfaceHeight);
+                            mSurfaceControl.setBufferSize(mSurfaceWidth, mSurfaceHeight);
                         }
                     } finally {
                         SurfaceControl.closeTransaction();
@@ -1122,6 +1126,13 @@
         }
     };
 
+    /**
+     * @hide
+     */
+    public SurfaceControl getSurfaceControl() {
+        return mSurfaceControl;
+    }
+
     class SurfaceControlWithBackground extends SurfaceControl {
         SurfaceControl mBackgroundControl;
         private boolean mOpaque = true;
@@ -1133,6 +1144,8 @@
 
             mBackgroundControl = b.setName("Background for -" + name)
                     .setFormat(OPAQUE)
+                    // Unset the buffer size of the background color layer.
+                    .setBufferSize(0, 0)
                     .setColorLayer(true)
                     .build();
             mOpaque = opaque;
@@ -1158,9 +1171,9 @@
         }
 
         @Override
-        public void setSize(int w, int h) {
-            super.setSize(w, h);
-            mBackgroundControl.setSize(w, h);
+        public void setBufferSize(int w, int h) {
+            super.setBufferSize(w, h);
+            // The background surface is a color layer so we do not set a size.
         }
 
         @Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0eaef5a..4b9cbff 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,7 +112,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
 import android.widget.Checkable;
 import android.widget.FrameLayout;
 import android.widget.ScrollBarDrawable;
@@ -4159,7 +4159,7 @@
         float mAlpha = 1f;
 
         /**
-         * The opacity of the view as manipulated by the Fade transition. This is a hidden
+         * The opacity of the view as manipulated by the Fade transition. This is a
          * property only used by transitions, which is composited with the other alpha
          * values to calculate the final visual alpha value.
          */
@@ -8138,7 +8138,7 @@
      * is visible.
      *
      * <p>The populated structure is then passed to the service through
-     * {@link IntelligenceManager#notifyViewAppeared(ViewStructure)}.
+     * {@link ContentCaptureManager#notifyViewAppeared(ViewStructure)}.
      *
      * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
      * <ul>
@@ -8915,7 +8915,7 @@
     }
 
     /**
-     * Helper used to notify the {@link IntelligenceManager} when the view is removed or
+     * Helper used to notify the {@link ContentCaptureManager} when the view is removed or
      * added, based on whether it's laid out and visible, and without knowing if the parent removed
      * it from the view hierarchy.
      *
@@ -8931,11 +8931,15 @@
      * </ol>
      */
     private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+        // First check if context has client, so it saves a service lookup when it doesn't
+        if (!mContext.isContentCaptureSupported()) return;
 
-        final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
-        if (im == null || !im.isContentCaptureEnabled()) return;
+        // Then check if it's enabled in the context...
+        final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+        if (cm == null || !cm.isContentCaptureEnabled()) return;
 
-        // NOTE: isImportantForContentCapture() is more expensive than im.isContentCaptureEnabled()
+        // ... and finally at the view level
+        // NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
         if (!isImportantForContentCapture()) return;
 
         if (appeared) {
@@ -8950,9 +8954,9 @@
                 return;
             }
             // All good: notify the manager...
-            final ViewStructure structure = im.newViewStructure(this);
+            final ViewStructure structure = cm.newViewStructure(this);
             onProvideContentCaptureStructure(structure, /* flags= */ 0);
-            im.notifyViewAppeared(structure);
+            cm.notifyViewAppeared(structure);
             // ...and set the flags
             mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
@@ -8969,7 +8973,7 @@
                 return;
             }
             // All good: notify the manager...
-            im.notifyViewDisappeared(getAutofillId());
+            cm.notifyViewDisappeared(getAutofillId());
             // ...and set the flags
             mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
             mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
@@ -10337,6 +10341,20 @@
     }
 
     /**
+     * Retrieves the single {@link WindowInsetsController} of the window this view is attached to.
+     *
+     * @return The {@link WindowInsetsController} or {@code null} if the view isn't attached to a
+     *         a window.
+     * @hide pending unhide
+     */
+    public @Nullable WindowInsetsController getWindowInsetsController() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mViewRootImpl.getInsetsController();
+        }
+        return null;
+    }
+
+    /**
      * @hide Compute the insets that should be consumed by this view and the ones
      * that should propagate to those under it.
      *
@@ -15729,15 +15747,12 @@
     }
 
     /**
-     * This property is hidden and intended only for use by the Fade transition, which
-     * animates it to produce a visual translucency that does not side-effect (or get
-     * affected by) the real alpha property. This value is composited with the other
-     * alpha value (and the AlphaAnimation value, when that is present) to produce
-     * a final visual translucency result, which is what is passed into the DisplayList.
-     *
-     * @hide
+     * This property is intended only for use by the Fade transition, which animates it
+     * to produce a visual translucency that does not side-effect (or get affected by)
+     * the real alpha property. This value is composited with the other alpha value
+     * (and the AlphaAnimation value, when that is present) to produce a final visual
+     * translucency result, which is what is passed into the DisplayList.
      */
-    @UnsupportedAppUsage
     public void setTransitionAlpha(float alpha) {
         ensureTransformationInfo();
         if (mTransformationInfo.mTransitionAlpha != alpha) {
@@ -15760,16 +15775,13 @@
     }
 
     /**
-     * This property is hidden and intended only for use by the Fade transition, which
-     * animates it to produce a visual translucency that does not side-effect (or get
-     * affected by) the real alpha property. This value is composited with the other
-     * alpha value (and the AlphaAnimation value, when that is present) to produce
-     * a final visual translucency result, which is what is passed into the DisplayList.
-     *
-     * @hide
+     * This property is intended only for use by the Fade transition, which animates
+     * it to produce a visual translucency that does not side-effect (or get affected
+     * by) the real alpha property. This value is composited with the other alpha
+     * value (and the AlphaAnimation value, when that is present) to produce a final
+     * visual translucency result, which is what is passed into the DisplayList.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
-    @UnsupportedAppUsage
     public float getTransitionAlpha() {
         return mTransformationInfo != null ? mTransformationInfo.mTransitionAlpha : 1;
     }
@@ -16288,9 +16300,17 @@
         }
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public void setAnimationMatrix(Matrix matrix) {
+    /**
+     * Changes the transformation matrix on the view. This is used in animation frameworks,
+     * such as {@link android.transition.Transition}. When the animation finishes, the matrix
+     * should be cleared by calling this method with <code>null</code> as the matrix parameter.
+     * Application developers should use transformation methods like {@link #setRotation(float)},
+     * {@link #setScaleX(float)}, {@link #setScaleX(float)}, {@link #setTranslationX(float)}}
+     * and {@link #setTranslationY(float)} (float)}} instead.
+     *
+     * @param matrix The matrix, null indicates that the matrix should be cleared.
+     */
+    public void setAnimationMatrix(@Nullable Matrix matrix) {
         invalidateViewProperty(true, false);
         mRenderNode.setAnimationMatrix(matrix);
         invalidateViewProperty(false, true);
@@ -21460,7 +21480,7 @@
      *         previous ones
      * {@hide}
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     protected boolean setFrame(int left, int top, int right, int bottom) {
         boolean changed = false;
 
@@ -21525,11 +21545,19 @@
     }
 
     /**
-     * Same as setFrame, but public and hidden. For use in {@link android.transition.ChangeBounds}.
-     * @hide
+     * Assign a size and position to this view.
+     *
+     * This method is meant to be used in animations only as it applies this position and size
+     * for the view only temporary and it can be changed back at any time by the layout.
+     *
+     * @param left Left position, relative to parent
+     * @param top Top position, relative to parent
+     * @param right Right position, relative to parent
+     * @param bottom Bottom position, relative to parent
+     *
+     * @see #setLeft(int), #setRight(int), #setTop(int), #setBottom(int)
      */
-    @UnsupportedAppUsage
-    public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+    public final void setLeftTopRightBottom(int left, int top, int right, int bottom) {
         setFrame(left, top, right, bottom);
     }
 
@@ -23537,6 +23565,16 @@
     }
 
     /**
+     * Get the identifier used for this view by the drawing system.
+     *
+     * @see RenderNode#getUniqueId()
+     * @return A long that uniquely identifies this view's drawing component
+     */
+    public long getUniqueDrawingId() {
+        return mRenderNode.getUniqueId();
+    }
+
+    /**
      * Returns this view's tag.
      *
      * @return the Object stored in this view as a tag, or {@code null} if not
@@ -24741,7 +24779,7 @@
         final SurfaceSession session = new SurfaceSession(root.mSurface);
         final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
                 .setName("drag surface")
-                .setSize(shadowSize.x, shadowSize.y)
+                .setBufferSize(shadowSize.x, shadowSize.y)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .build();
         final Surface surface = new Surface();
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 767cd33..d03d97e 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -393,7 +393,7 @@
                 case HAS_PERMANENT_MENU_KEY_AUTODETECT: {
                     IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
                     try {
-                        sHasPermanentMenuKey = !wm.hasNavigationBar();
+                        sHasPermanentMenuKey = !wm.hasNavigationBar(context.getDisplayId());
                         sHasPermanentMenuKeySet = true;
                     } catch (RemoteException ex) {
                         sHasPermanentMenuKey = false;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1e91aa8..741510e 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7042,10 +7042,7 @@
      * suppression is disabled with a later call to suppressLayout(false).
      * When layout suppression is disabled, a requestLayout() call is sent
      * if layout() was attempted while layout was being suppressed.
-     *
-     * @hide
      */
-    @UnsupportedAppUsage
     public void suppressLayout(boolean suppress) {
         mSuppressLayout = suppress;
         if (!suppress) {
@@ -7061,8 +7058,6 @@
      * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
      *
      * @return true if layout calls are currently suppressed, false otherwise.
-     *
-     * @hide
      */
     public boolean isLayoutSuppressed() {
         return mSuppressLayout;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 484c6f3..cb47886 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -161,6 +161,24 @@
     private static final boolean MT_RENDERER_AVAILABLE = true;
 
     /**
+     * If set to true, the view system will switch from using rectangles retrieved from window to
+     * dispatch to the view hierarchy to using {@link InsetsController}, that derives the insets
+     * directly from the full configuration, enabling richer information about the insets state, as
+     * well as new APIs to control it frame-by-frame, and synchronize animations with it.
+     * <p>
+     * Only switch this to true once the new insets system is productionized and the old APIs are
+     * fully migrated over.
+     */
+    private static final String USE_NEW_INSETS_PROPERTY = "persist.wm.new_insets";
+
+    /**
+     * @see #USE_NEW_INSETS_PROPERTY
+     * @hide
+     */
+    public static final boolean USE_NEW_INSETS =
+            SystemProperties.getBoolean(USE_NEW_INSETS_PROPERTY, false);
+
+    /**
      * Set this system property to true to force the view hierarchy to render
      * at 60 Hz. This can be used to measure the potential framerate.
      */
@@ -432,6 +450,8 @@
     boolean mAdded;
     boolean mAddedTouchMode;
 
+    final Rect mTmpFrame = new Rect();
+
     // These are accessed by multiple threads.
     final Rect mWinFrame; // frame given by window manager.
 
@@ -444,6 +464,7 @@
     final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
             new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
+    private InsetsState mPendingInsets = new InsetsState();
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
@@ -531,6 +552,8 @@
             InputEventConsistencyVerifier.isInstrumentationEnabled() ?
                     new InputEventConsistencyVerifier(this, 0) : null;
 
+    private final InsetsController mInsetsController = new InsetsController();
+
     static final class SystemUiVisibilityInfo {
         int seq;
         int globalVisibility;
@@ -797,9 +820,11 @@
                     mAttachInfo.mRecomputeGlobalAttributes = true;
                     collectViewAttributes();
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
-                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
+                            getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
+                            mInsetsController.getState());
+                    setFrame(mTmpFrame);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -826,6 +851,7 @@
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
                 mPendingAlwaysConsumeNavBar = mAttachInfo.mAlwaysConsumeNavBar;
+                mPendingInsets = mInsetsController.getState();
                 if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
                 if (res < WindowManagerGlobal.ADD_OKAY) {
                     mAttachInfo.mRootView = null;
@@ -1473,31 +1499,22 @@
 
         mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                 .setName("Bounds for - " + getTitle().toString())
-                .setSize(mWidth, mHeight)
                 .build();
 
-        setBoundsSurfaceSizeAndCrop();
+        setBoundsSurfaceCrop();
         mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
                     .show(mBoundsSurfaceControl)
                     .apply();
         mBoundsSurface.copyFrom(mBoundsSurfaceControl);
     }
 
-    private void setBoundsSurfaceSizeAndCrop() {
+    private void setBoundsSurfaceCrop() {
         // mWinFrame is already adjusted for surface insets. So offset it and use it as
         // the cropping bounds.
         mTempBoundsRect.set(mWinFrame);
         mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
                 mWindowAttributes.surfaceInsets.top);
         mTransaction.setWindowCrop(mBoundsSurfaceControl, mTempBoundsRect);
-
-        // Expand the bounds by the surface insets to get the size of surface.
-        mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left,
-                -mWindowAttributes.surfaceInsets.top,
-                -mWindowAttributes.surfaceInsets.right,
-                -mWindowAttributes.surfaceInsets.bottom);
-        mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(),
-                mTempBoundsRect.height());
     }
 
     /**
@@ -1506,7 +1523,7 @@
      */
     private void updateBoundsSurface() {
         if (mBoundsSurfaceControl != null && mSurface.isValid()) {
-            setBoundsSurfaceSizeAndCrop();
+            setBoundsSurfaceCrop();
             mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
                     mSurface, mSurface.getNextFrameNumber())
                     .apply();
@@ -1780,7 +1797,8 @@
             Rect stableInsets = mDispatchStableInsets;
             DisplayCutout displayCutout = mDispatchDisplayCutout;
             // For dispatch we preserve old logic, but for direct requests from Views we allow to
-            // immediately use pending insets.
+            // immediately use pending insets. This is such that getRootWindowInsets returns the
+            // result from the layout hint before we ran a traversal shortly after adding a window.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
                         !mPendingStableInsets.equals(stableInsets) ||
@@ -1797,10 +1815,16 @@
             }
             contentInsets = ensureInsetsNonNegative(contentInsets, "content");
             stableInsets = ensureInsetsNonNegative(stableInsets, "stable");
-            mLastWindowInsets = new WindowInsets(contentInsets,
-                    null /* windowDecorInsets */, stableInsets,
-                    mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            if (USE_NEW_INSETS) {
+                mLastWindowInsets = mInsetsController.calculateInsets(
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            } else {
+                mLastWindowInsets = new WindowInsets(contentInsets,
+                        null /* windowDecorInsets */, stableInsets,
+                        mContext.getResources().getConfiguration().isScreenRound(),
+                        mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
+            }
         }
         return mLastWindowInsets;
     }
@@ -1828,6 +1852,10 @@
         host.dispatchApplyWindowInsets(insets);
     }
 
+    InsetsController getInsetsController() {
+        return mInsetsController;
+    }
+
     private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
         return lp.type == TYPE_STATUS_BAR_PANEL
                 || lp.type == TYPE_INPUT_METHOD
@@ -1916,7 +1944,6 @@
             // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888
             // However, windows are now always 32 bits by default, so choose 32 bits
             mAttachInfo.mUse32BitDrawingCache = true;
-            mAttachInfo.mHasWindowFocus = false;
             mAttachInfo.mWindowVisibility = viewVisibility;
             mAttachInfo.mRecomputeGlobalAttributes = false;
             mLastConfigurationFromResources.setTo(config);
@@ -2000,6 +2027,9 @@
                 if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                     insetsChanged = true;
                 }
+                if (!mPendingInsets.equals(mInsetsController.getState())) {
+                    insetsChanged = true;
+                }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2193,6 +2223,8 @@
                         mAttachInfo.mStableInsets);
                 final boolean cutoutChanged = !mPendingDisplayCutout.equals(
                         mAttachInfo.mDisplayCutout);
+                final boolean insetsStateChanged = !mPendingInsets.equals(
+                        mInsetsController.getState());
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -2230,6 +2262,10 @@
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
                 }
+                if (insetsStateChanged) {
+                    mInsetsController.setState(mPendingInsets);
+                    contentInsetsChanged = true;
+                }
                 if (contentInsetsChanged || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested
                         || mLastOverscanRequested != mAttachInfo.mOverscanRequested
@@ -2675,7 +2711,6 @@
     }
 
     private void maybeHandleWindowMove(Rect frame) {
-
         // TODO: Well, we are checking whether the frame has changed similarly
         // to how this is done for the insets. This is however incorrect since
         // the insets and the frame are translated. For example, the old frame
@@ -4180,6 +4215,8 @@
     private final static int MSG_UPDATE_POINTER_ICON = 27;
     private final static int MSG_POINTER_CAPTURE_CHANGED = 28;
     private final static int MSG_DRAW_FINISHED = 29;
+    private final static int MSG_INSETS_CHANGED = 30;
+    private final static int MSG_INSETS_CONTROL_CHANGED = 31;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -4235,6 +4272,8 @@
                     return "MSG_POINTER_CAPTURE_CHANGED";
                 case MSG_DRAW_FINISHED:
                     return "MSG_DRAW_FINISHED";
+                case MSG_INSETS_CHANGED:
+                    return "MSG_INSETS_CHANGED";
             }
             return super.getMessageName(message);
         }
@@ -4315,7 +4354,7 @@
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
-                        mWinFrame.set((Rect) args.arg1);
+                        setFrame((Rect) args.arg1);
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
@@ -4338,16 +4377,36 @@
                         requestLayout();
                     }
                     break;
+                case MSG_INSETS_CHANGED:
+                    mPendingInsets = (InsetsState) msg.obj;
+
+                    // TODO: Full traversal not needed here.
+                    if (USE_NEW_INSETS) {
+                        requestLayout();
+                    }
+                    break;
+                case MSG_INSETS_CONTROL_CHANGED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    mPendingInsets = (InsetsState) args.arg1;
+                    mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
+
+                    // TODO: Full traversal not necessarily needed here.
+                    if (USE_NEW_INSETS) {
+                        requestLayout();
+                    }
+                    break;
+                }
                 case MSG_WINDOW_MOVED:
                     if (mAdded) {
                         final int w = mWinFrame.width();
                         final int h = mWinFrame.height();
                         final int l = msg.arg1;
                         final int t = msg.arg2;
-                        mWinFrame.left = l;
-                        mWinFrame.right = l + w;
-                        mWinFrame.top = t;
-                        mWinFrame.bottom = t + h;
+                        mTmpFrame.left = l;
+                        mTmpFrame.right = l + w;
+                        mTmpFrame.top = t;
+                        mTmpFrame.bottom = t + h;
+                        setFrame(mTmpFrame);
 
                         mPendingBackDropFrame.set(mWinFrame);
                         maybeHandleWindowMove(mWinFrame);
@@ -6733,9 +6792,9 @@
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
-                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
+                mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
-                mPendingMergedConfiguration, mSurface);
+                mPendingMergedConfiguration, mSurface, mPendingInsets);
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6745,15 +6804,22 @@
         }
 
         if (mTranslator != null) {
-            mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+            mTranslator.translateRectInScreenToAppWinFrame(mTmpFrame);
             mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
             mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
         }
+        setFrame(mTmpFrame);
+
         return relayoutResult;
     }
 
+    private void setFrame(Rect frame) {
+        mWinFrame.set(frame);
+        mInsetsController.onFrameChanged(frame);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -6856,6 +6922,8 @@
 
         mChoreographer.dump(prefix, writer);
 
+        mInsetsController.dump(prefix, writer);
+
         writer.print(prefix); writer.println("View Hierarchy:");
         dumpViewHierarchy(innerPrefix, writer, mView);
     }
@@ -7064,6 +7132,18 @@
         mHandler.sendMessage(msg);
     }
 
+    private void dispatchInsetsChanged(InsetsState insetsState) {
+        mHandler.obtainMessage(MSG_INSETS_CHANGED, insetsState).sendToTarget();
+    }
+
+    private void dispatchInsetsControlChanged(InsetsState insetsState,
+            InsetsSourceControl[] activeControls) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = insetsState;
+        args.arg2 = activeControls;
+        mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
+    }
+
     public void dispatchMoved(int newX, int newY) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
@@ -8127,6 +8207,23 @@
         }
 
         @Override
+        public void insetsChanged(InsetsState insetsState) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchInsetsChanged(insetsState);
+            }
+        }
+
+        @Override
+        public void insetsControlChanged(InsetsState insetsState,
+                InsetsSourceControl[] activeControls) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls);
+            }
+        }
+
+        @Override
         public void moved(int newX, int newY) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c1e94d8..58ab817 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2410,4 +2410,11 @@
     public boolean isCloseOnSwipeEnabled() {
         return mCloseOnSwipeEnabled;
     }
+
+    /**
+     * @return The {@link WindowInsetsController} associated with this window
+     * @see View#getWindowInsetsController()
+     * @hide pending unhide
+     */
+    public abstract @NonNull WindowInsetsController getInsetsController();
 }
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index a8debbd..572d331 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -18,13 +18,17 @@
 package android.view;
 
 import android.annotation.NonNull;
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.view.inputmethod.InputMethod;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -807,4 +811,69 @@
                     mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
         }
     }
+
+    /**
+     * Class that defines different types of sources causing window insets.
+     * @hide pending unhide
+     */
+    public static final class Type {
+
+        static final int TOP_BAR = 0x1;
+        static final int IME = 0x2;
+        static final int SIDE_BARS = 0x4;
+        static final int WINDOW_DECOR = 0x8;
+
+        private Type() {
+        }
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, value = { TOP_BAR, IME, SIDE_BARS, WINDOW_DECOR })
+        public @interface InsetType {
+        }
+
+        /**
+         * @return An inset type representing the top bar of a window, which can be the status
+         *         bar on handheld-like devices as well as a caption bar.
+         */
+        public static @InsetType int topBar() {
+            return TOP_BAR;
+        }
+
+        /**
+         * @return An inset type representing the window of an {@link InputMethod}.
+         */
+        public static @InsetType int ime() {
+            return IME;
+        }
+
+        /**
+         * @return An inset type representing any system bars that are not {@link #topBar()}.
+         */
+        public static @InsetType int sideBars() {
+            return SIDE_BARS;
+        }
+
+        /**
+         * @return An inset type representing decor that is being app-controlled.
+         */
+        public static @InsetType int windowDecor() {
+            return WINDOW_DECOR;
+        }
+
+        /**
+         * @return All system bars. Includes {@link #topBar()} as well as {@link #sideBars()}, but
+         *         not {@link #ime()}.
+         */
+        public static @InsetType int systemBars() {
+            return TOP_BAR | SIDE_BARS;
+        }
+
+        /**
+         * @return All inset types combined.
+         */
+        public static @InsetType int all() {
+            return 0xFFFFFFFF;
+        }
+    }
 }
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
new file mode 100644
index 0000000..7be5f2e
--- /dev/null
+++ b/core/java/android/view/WindowInsetsController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import android.view.WindowInsets.Type.InsetType;
+
+/**
+ * Interface to control windows that generate insets.
+ *
+ * TODO Needs more information and examples once the API is more baked.
+ * @hide pending unhide
+ */
+public interface WindowInsetsController {
+
+    /**
+     * Makes a set of windows that cause insets appear on screen.
+     * <p>
+     * Note that if the window currently doesn't have control over a certain type, it will apply the
+     * change as soon as the window gains control. The app can listen to the event by observing
+     * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+     * {@link WindowInsets}.
+     *
+     * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+     *              would like to make appear on screen.
+     */
+    void show(@InsetType int types);
+
+    /**
+     * Makes a set of windows causing insets disappear.
+     * <p>
+     * Note that if the window currently doesn't have control over a certain type, it will apply the
+     * change as soon as the window gains control. The app can listen to the event by observing
+     * {@link View#onApplyWindowInsets} and checking visibility with "TODO at method" in
+     * {@link WindowInsets}.
+     *
+     * @param types A bitmask of {@link WindowInsets.Type.InsetType} specifying what windows the app
+     *              would like to make disappear.
+     */
+    void hide(@InsetType int types);
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8d8a370..45c3651 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1752,15 +1752,6 @@
         public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
 
         /**
-         * If this flag is set on the window, window manager will acquire a sleep token that puts
-         * all activities to sleep as long as this window is visible. When this flag is set, the
-         * window needs to occlude all activity windows.
-         * @hide
-         */
-        @RequiresPermission(permission.DEVICE_POWER)
-        public static final int PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN = 0x00200000;
-
-        /**
          * Flag to indicate that this window should be considered a screen decoration similar to the
          * nav bar and status bar. This will cause this window to affect the window insets reported
          * to other windows when it is visible.
@@ -1872,10 +1863,6 @@
                         equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
                         name = "IS_ROUNDED_CORNERS_OVERLAY"),
                 @ViewDebug.FlagToString(
-                        mask = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
-                        equals = PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN,
-                        name = "ACQUIRES_SLEEP_TOKEN"),
-                @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
                         equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
                         name = "IS_SCREEN_DECOR"),
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 260e938..16bafe2 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -44,6 +44,7 @@
     int PRESENCE_EXTERNAL = 1 << 1;
 
     // Navigation bar position values
+    int NAV_BAR_INVALID = -1;
     int NAV_BAR_LEFT = 1 << 0;
     int NAV_BAR_RIGHT = 1 << 1;
     int NAV_BAR_BOTTOM = 1 << 2;
diff --git a/core/java/android/view/inspector/ChildTraverser.java b/core/java/android/view/inspector/ChildTraverser.java
deleted file mode 100644
index b775de5..0000000
--- a/core/java/android/view/inspector/ChildTraverser.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 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.view.inspector;
-
-import android.annotation.NonNull;
-
-/**
- * Interface for visiting all the child nodes of an inspectable object.
- *
- * Inspectable objects may return a collection of children as an array, an {@link Iterable} or an
- * {@link java.util.Iterator}. This provides a unified API for traversing across all the children
- * of an inspectable node.
- *
- * This interface is consumed by {@link InspectionHelper#traverseChildren(Object, ChildTraverser)}
- * and may be implemented as a lambda.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @hide
- */
-@FunctionalInterface
-public interface ChildTraverser {
-    /**
-     * Visit one child object of a parent inspectable object.
-     *
-     * The iteration interface will filter null values out before passing them to this method, but
-     * some child objects may not be inspectable. It is up to the implementor to determine their
-     * inspectablity and what to do with them.
-     *
-     * @param child A child object, guaranteed not to be null.
-     */
-    void traverseChild(@NonNull Object child);
-}
diff --git a/core/java/android/view/inspector/InspectableChildren.java b/core/java/android/view/inspector/InspectableChildren.java
deleted file mode 100644
index de8fa29..0000000
--- a/core/java/android/view/inspector/InspectableChildren.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 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.view.inspector;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-/**
- * Marks a getter for an inspectable node's inspectable children.
- *
- * This annotation can be applied to any getter that returns a collection of objects, either an
- * array, an {@link Iterable} or a {@link java.util.Iterator}. The getter may return null, which
- * will be treated as an empty collection. Additionally, the inspector will discard any null
- * entries in the collection.
- *
- * By default, this annotation is inherited. At runtime, the inspector introspects on the class
- * hierachy and uses the annotated getter from the bottommost class, if different from any
- * annoated getters of the parent class. If a class inherits from a parent class with an annotated
- * getter, but does not include this annotation, the child class will be traversed using the
- * getter annotated on the parent. This holds true even if the child class overrides the getter.
- *
- * @see InspectionHelper#traverseChildren(Object, ChildTraverser)
- * @see InspectionHelper#hasChildTraversal()
- * @hide
- */
-@Target({METHOD})
-@Retention(SOURCE)
-public @interface InspectableChildren {
-}
diff --git a/core/java/android/view/inspector/InspectableNodeName.java b/core/java/android/view/inspector/InspectableNodeName.java
index 716409c..ea94ad4 100644
--- a/core/java/android/view/inspector/InspectableNodeName.java
+++ b/core/java/android/view/inspector/InspectableNodeName.java
@@ -34,7 +34,7 @@
  * This annotation does not inherit. If a class extends an annotated parent class, but does not
  * annotate itself, its node name will be inferred from its Java name.
  *
- * @see InspectionHelper#getNodeName()
+ * @see InspectionCompanion#getNodeName()
  * @hide
  */
 @Target({TYPE})
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index b0fd503..5b957156 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -17,8 +17,11 @@
 package android.view.inspector;
 
 import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.content.res.ResourceId;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
@@ -31,8 +34,8 @@
  * but on a different getter, the inspector will use the child's getter when inspecting instances
  * of the child, and the parent's otherwise.
  *
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @see InspectionHelper#readProperties(Object, PropertyReader)
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
  * @hide
  */
 @Target({METHOD})
@@ -46,5 +49,171 @@
      *
      * @return The name of the property.
      */
-    String value() default "";
+    String name() default "";
+
+    /**
+     * If the property is inflated from XML, the resource ID of its XML attribute.
+     *
+     * If left as {ID_NULL}, and {@link #hasAttributeId()} is true, the attribute ID will be
+     * inferred from {@link #name()}.
+     *
+     * @return The attribute ID of the property or {@link ResourceId#ID_NULL}
+     */
+    int attributeId() default ResourceId.ID_NULL;
+
+    /**
+     * If this property has an attribute ID.
+     *
+     * Set to false if the annotated property does not have an attribute ID, that is, it is not
+     * inflated from an XML attribute. This will prevent the automatic inference of the attribute
+     * ID if {@link #attributeId()} is set to {@link ResourceId#ID_NULL}.
+     *
+     * @return Whether to infer an attribute ID if not supplied
+     */
+    boolean hasAttributeId() default true;
+
+    /**
+     * Specify how to interpret a value type packed into a primitive integer.
+     *
+     * @return A {@link ValueType}
+     */
+    ValueType valueType() default ValueType.INFERRED;
+
+    /**
+     * For enumerations packed into primitive {int} properties, map the values to string names.
+     *
+     * Note that {@link #enumMapping()} cannot be used simultaneously with {@link #flagMapping()}.
+     *
+     * @return An array of {@link EnumMap}, empty if not applicable
+     * @see android.annotation.IntDef
+     * @see IntEnumMapping
+     */
+    EnumMap[] enumMapping() default {};
+
+    /**
+     * For flags packed into primitive {int} properties, model the string names of the flags.
+     *
+     * Note that {@link #flagMapping()} cannot be used simultaneously with {@link #enumMapping()}.
+     *
+     * @return An array of {@link FlagMap}, empty if not applicable
+     * @see android.annotation.IntDef
+     * @see IntFlagMapping
+     */
+    FlagMap[] flagMapping() default {};
+
+
+    /**
+     * One entry in an enumeration packed into a primitive {int}.
+     *
+     * @see IntEnumMapping
+     * @hide
+     */
+    @Target({TYPE})
+    @Retention(SOURCE)
+    @interface EnumMap {
+        /**
+         * The string name of this enumeration value.
+         *
+         * @return A string name
+         */
+        String name();
+
+        /**
+         * The integer value of this enumeration value.
+         *
+         * @return An integer value
+         */
+        int value();
+    }
+
+    /**
+     * One flag value of many that may be packed into a primitive {int}.
+     *
+     * @see IntFlagMapping
+     * @hide
+     */
+    @Target({TYPE})
+    @Retention(SOURCE)
+    @interface FlagMap {
+        /**
+         * The string name of this flag.
+         *
+         * @return A string name
+         */
+        String name();
+
+        /**
+         * A target value that the property's value must equal after masking.
+         *
+         * If a mask is not supplied (i.e., {@link #mask()} is 0), the target will be reused as the
+         * mask. This handles the common case where no flags mutually exclude each other.
+         *
+         * @return The target value to compare against
+         */
+        int target();
+
+        /**
+         * A mask that the property will be bitwise anded with before comparing to the target.
+         *
+         * If set to 0 (the default), the value of {@link #target()} will be used as a mask. Zero
+         * was chosen as the default since bitwise and with zero is always zero.
+         *
+         * @return A mask, or 0 to use the target as a mask
+         */
+        int mask() default 0;
+    }
+
+    /**
+     * The type of value packed into a primitive {int}.
+     *
+     * @hide
+     */
+    enum ValueType {
+        /**
+         * No special handling, property is considered to be a numeric value.
+         */
+        NONE,
+
+        /**
+         * The default the annotation processor infers the value type from context.
+         */
+        INFERRED,
+
+        /**
+         * Value packs an enumeration.
+         *
+         * This is inferred if {@link #enumMapping()} is specified.
+         *
+         * @see EnumMap
+         */
+        INT_ENUM,
+
+        /**
+         * Value packs flags, of which many may be enabled at once.
+         *
+         * This is inferred if {@link #flagMapping()} is specified.
+         *
+         * @see FlagMap
+         */
+        INT_FLAG,
+
+        /**
+         * Value packs color information.
+         *
+         * This is inferred from {@link android.annotation.ColorInt}, or
+         * {@link android.annotation.ColorLong} on the getter method.
+         *
+         * @see android.graphics.Color
+         */
+        COLOR,
+
+        /**
+         * Value packs gravity information.
+         *
+         * This type is not inferred, and is non-trivial to represent using {@link FlagMap}.
+         *
+         * @see android.view.Gravity
+         */
+        GRAVITY
+    }
 }
diff --git a/core/java/android/view/inspector/InspectionCompanion.java b/core/java/android/view/inspector/InspectionCompanion.java
new file mode 100644
index 0000000..62d769b
--- /dev/null
+++ b/core/java/android/view/inspector/InspectionCompanion.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 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.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * An interface for companion objects used to inspect views.
+ *
+ * Inspection companions only need to handle the properties and node name of the specific class
+ * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
+ * one instance of each inspection companion, and handles visiting them in the correct inheritance
+ * order for each type it inspects.
+ *
+ * Properties are read from the top of the type tree to the bottom, so that classes that override
+ * a property in their parent class can overwrite it in the reader. In general, properties will
+ * cleanly inherit through their getters, and the inspector runtime will read the properties of a
+ * parent class via the parent's inspection companion, and the child companion will only read
+ * properties added or changed since the parent was defined.
+ *
+ * Only one child traversal is considered for each class. If a descendant class defines a
+ * different child traversal than its parent, only the bottom traversal is used. If a class does
+ * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
+ * traversal will be used.
+ *
+ * @param <T> The type of inspectable this is the companion to
+ */
+public interface InspectionCompanion<T> {
+    /**
+     * Map the string names of the properties this companion knows about to integer IDs.
+     *
+     * Each companion is responsible for storing the integer IDs of all its properties. This is the
+     * only method that is allowed to modify the stored IDs.
+     *
+     * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
+     * undefined behavior.
+     *
+     * @param propertyMapper A {@link PropertyMapper} maps string names to IDs.
+     */
+    void mapProperties(@NonNull PropertyMapper propertyMapper);
+
+    /**
+     * Read the values of an instance of this companion's type into a {@link PropertyReader}.
+     *
+     * This method needs to return the property IDs stored by
+     * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
+     * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
+     * called before {mapProperties}.
+     *
+     * @param inspectable A object of type {T} to read the properties of.
+     * @param propertyReader An object which receives the property IDs and values.
+     */
+    void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
+
+    /**
+     * Get an optional name to display to developers for inspection nodes of this companion's type.
+     *
+     * The default implementation returns null, which will cause the runtime to use the class's
+     * simple name as defined by {@link Class#getSimpleName()} as the node name.
+     *
+     * If the type of this companion is inflated from XML, this method should be overridden to
+     * return the string used as the tag name for this type in XML.
+     *
+     * @return A string to use as the node name, or null to use the simple class name fallback.
+     */
+    @Nullable
+    default String getNodeName() {
+        return null;
+    }
+
+    /**
+     * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
+     * {@link #mapProperties(PropertyMapper)}.
+     */
+    class UninitializedPropertyMapException extends RuntimeException {
+        public UninitializedPropertyMapException() {
+            super("Unable to read properties of an inspectable before mapping their IDs.");
+        }
+    }
+}
diff --git a/core/java/android/view/inspector/InspectionHelper.java b/core/java/android/view/inspector/InspectionHelper.java
deleted file mode 100644
index 27a9704..0000000
--- a/core/java/android/view/inspector/InspectionHelper.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright 2018 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.view.inspector;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An interface for companion objects used to inspect views.
- *
- * Inspection helpers only need to handle the properties, name and traversal of the specific class
- * they are defined for, not anything from a parent class. At runtime, the inspector instantiates
- * one instance of each inspection helper, and handles visiting them in the correct inheritance
- * order for each type it inspects.
- *
- * Properties are read from the top of the type tree to the bottom, so that classes that override
- * a property in their parent class can overwrite it in the reader. In general, properties will
- * cleanly inherit through their getters, and the inspector runtime will read the properties of a
- * parent class via the parent's inspection helper, and the child helper will only read properties
- * added or changed since the parent was defined.
- *
- * Only one child traversal is considered for each class. If a descendant class defines a
- * different child traversal than its parent, only the bottom traversal is used. If a class does
- * not define its own child traversal, but one of its ancestors does, the bottom-most ancestor's
- * traversal will be used.
- *
- * @param <T> The type of inspectable this helper operates on
- * @hide
- */
-public interface InspectionHelper<T> {
-    /**
-     * Map the string names of the properties this helper knows about to integer IDs.
-     *
-     * Each helper is responsible for storing the integer IDs of all its properties. This is the
-     * only method that is allowed to modify the stored IDs.
-     *
-     * Calling {@link #readProperties(T, PropertyReader)} before calling this results in
-     * undefined behavior.
-     *
-     * @param propertyMapper A {@link PropertyMapper} or lambda which maps string names to IDs.
-     */
-    void mapProperties(@NonNull PropertyMapper propertyMapper);
-
-    /**
-     * Read the values of an instance of this helper's type into a {@link PropertyReader}.
-     *
-     * This method needs to return the property IDs stored by
-     * {@link #mapProperties(PropertyMapper)}. Implementations should track if their properties
-     * have been mapped and throw a {@link UninitializedPropertyMapException} if this method is
-     * called before {mapProperties}.
-     *
-     * @param inspectable A object of type {@link T} to read the properties of.
-     * @param propertyReader An object which receives the property IDs and values.
-     */
-    void readProperties(@NonNull T inspectable, @NonNull PropertyReader propertyReader);
-
-    /**
-     * Query if this inspectable type can potentially have child nodes.
-     *
-     * E.g.: any descendant of {@link android.view.ViewGroup} can have child nodes, but a leaf
-     * view like {@link android.widget.ImageView} may not.
-     *
-     * The default implementation always returns false. If an implementing class overrides this, it
-     * should also define {@link #traverseChildren(T, ChildTraverser)}.
-     *
-     * @return True if this inspectable type can potentially have child nodes, false otherwise.
-     */
-    default boolean hasChildTraversal() {
-        return false;
-    }
-
-    /**
-     * Traverse the child nodes of an instance of this helper's type into a {@link ChildTraverser}.
-     *
-     * This provides the ability to traverse over a variety of collection APIs (e.g.: arrays,
-     * {@link Iterable}, or {@link java.util.Iterator}) in a uniform fashion. The traversal must be
-     * in the order defined by this helper's type. If the getter returns null, the helper must
-     * treat it as an empty collection.
-     *
-     * The default implementation throws a {@link NoChildTraversalException}. If
-     * {@link #hasChildTraversal()} returns is overriden to return true, it is expected that the
-     * implementing class will also override this method and provide a traversal.
-     *
-     * @param inspectable An object of type {@link T} to traverse the child nodes of.
-     * @param childTraverser A {@link ChildTraverser} or lamba to receive the children in order.
-     * @throws NoChildTraversalException If there is no defined child traversal
-     */
-    default void traverseChildren(
-            @NonNull T inspectable,
-            @SuppressWarnings("unused") @NonNull ChildTraverser childTraverser) {
-        throw new NoChildTraversalException(inspectable.getClass());
-    }
-
-    /**
-     * Get an optional name to display to developers for inspection nodes of this helper's type.
-     *
-     * The default implementation returns null, which will cause the runtime to use the class's
-     * simple name as defined by {@link Class#getSimpleName()} as the node name.
-     *
-     * If the type of this helper is inflated from XML, this method should be overridden to return
-     * the string used as the tag name for this type in XML.
-     *
-     * @return A string to use as the node name, or null to use the simple class name fallback.
-     */
-    @Nullable
-    default String getNodeName() {
-        return null;
-    }
-
-    /**
-     * Thrown by {@link #readProperties(Object, PropertyReader)} if called before
-     * {@link #mapProperties(PropertyMapper)}.
-     */
-    class UninitializedPropertyMapException extends RuntimeException {
-        public UninitializedPropertyMapException() {
-            super("Unable to read properties of an inspectable before mapping their IDs.");
-        }
-    }
-
-    /**
-     * Thrown by {@link #traverseChildren(Object, ChildTraverser)} if no child traversal exists.
-     */
-    class NoChildTraversalException extends RuntimeException {
-        public NoChildTraversalException(Class cls) {
-            super(String.format(
-                    "Class %s does not have a defined child traversal. Cannot traverse children.",
-                    cls.getCanonicalName()
-            ));
-        }
-    }
-}
diff --git a/core/java/android/view/inspector/IntEnumMapping.java b/core/java/android/view/inspector/IntEnumMapping.java
new file mode 100644
index 0000000..69f6dce
--- /dev/null
+++ b/core/java/android/view/inspector/IntEnumMapping.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018 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.view.inspector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to string names for properties that encode enumerations.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for enumerations packed into primitive {int} properties.
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntEnum(String, int, IntEnumMapping)
+ */
+public final class IntEnumMapping {
+    private final Value[] mValues;
+
+    /**
+     * Map from a property value to a string name.
+     *
+     * @param value The value of a property
+     * @return The name of the enumeration value, null if the value is not mapped
+     */
+    @Nullable
+    public String nameOf(int value) {
+        for (Value valueTuple : mValues) {
+            if (valueTuple.mValue == value) {
+                return valueTuple.mName;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Create a new instance from a builder.
+     *
+     * This constructor is private, use {@link Builder#build()} instead.
+     *
+     * @param builder A builder to create from
+     */
+    private IntEnumMapping(Builder builder) {
+        mValues = builder.mValues.toArray(new Value[builder.mValues.size()]);
+    }
+
+    /**
+     * A builder for {@link IntEnumMapping}
+     */
+    public static final class Builder {
+        private final ArrayList<Value> mValues;
+
+        public Builder() {
+            mValues = new ArrayList<>();
+        }
+
+        /**
+         * Add a new entry to this mapping.
+         *
+         * @param name Name of the enumeration value
+         * @param value Int value of the enumeration value
+         * @return This builder
+         */
+        @NonNull
+        public Builder addValue(@NonNull String name, int value) {
+            mValues.add(new Value(name, value));
+            return this;
+        }
+
+        /**
+         * Clear the builder, allowing for recycling.
+         */
+        public void clear() {
+            mValues.clear();
+        }
+
+        /**
+         * Build a new {@link IntEnumMapping} from this builder
+         *
+         * @return A new mapping
+         */
+        @NonNull
+        public IntEnumMapping build() {
+            return new IntEnumMapping(this);
+        }
+    }
+
+    /**
+     * Inner class that holds the name and value of an enumeration value.
+     */
+    private static final class Value {
+        @NonNull private final String mName;
+        private final int mValue;
+
+        private Value(@NonNull String name, int value) {
+            mName = name;
+            mValue = value;
+        }
+    }
+}
diff --git a/core/java/android/view/inspector/IntFlagMapping.java b/core/java/android/view/inspector/IntFlagMapping.java
new file mode 100644
index 0000000..dcb87e1
--- /dev/null
+++ b/core/java/android/view/inspector/IntFlagMapping.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2018 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.view.inspector;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+
+/**
+ * Maps the values of an {int} property to arrays of string for properties that encode flags.
+ *
+ * An {@link InspectionCompanion} may provide an instance of this class to a {@link PropertyMapper}
+ * for flag values packed into primitive {int} properties.
+ *
+ * Each flag has a
+ *
+ * This class is immutable, and must be constructed by a {@link Builder}.
+ *
+ * @see PropertyMapper#mapIntFlag(String, int, IntFlagMapping)
+ */
+public final class IntFlagMapping {
+    private final Flag[] mFlags;
+
+    /**
+     * Get an array of the names of enabled flags for a given property value.
+     *
+     * @param value The value of the property
+     * @return The names of the enabled flags
+     */
+    @NonNull
+    public String[] namesOf(int value) {
+        ArrayList<String> enabledFlagNames = new ArrayList<>(mFlags.length);
+
+        for (Flag flag : mFlags) {
+            if (flag.isEnabledFor(value)) {
+                enabledFlagNames.add(flag.mName);
+            }
+        }
+
+        return enabledFlagNames.toArray(new String[enabledFlagNames.size()]);
+    }
+
+    /**
+     * Create a new instance from a builder.
+     *
+     * This constructor is private, use {@link Builder#build()} instead.
+     *
+     * @param builder A builder to create from
+     */
+    private IntFlagMapping(Builder builder) {
+        mFlags = builder.mFlags.toArray(new Flag[builder.mFlags.size()]);
+    }
+
+    /**
+     * A builder for {@link IntFlagMapping}.
+     */
+    public static final class Builder {
+        private ArrayList<Flag> mFlags;
+
+        public Builder() {
+            mFlags = new ArrayList<>();
+        }
+
+        /**
+         * Add a new flag without a mask.
+         *
+         * The target value will be used as a mask, to handle the common case where flag values
+         * are not mutually exclusive. The flag will be considered enabled for a property value if
+         * the result of bitwise anding the target and the value equals the target, that is:
+         * {(value & target) == target}.
+         *
+         * @param name The name of the flag
+         * @param target The value to compare against
+         * @return This builder
+         */
+        @NonNull
+        public Builder addFlag(@NonNull String name, int target) {
+            mFlags.add(new Flag(name, target, target));
+            return this;
+        }
+
+        /**
+         * Add a new flag with a mask.
+         *
+         * The flag will be considered enabled for a property value if the result of bitwise anding
+         * the value and the mask equals the target, that is: {(value & mask) == target}.
+         *
+         * @param name The name of the flag
+         * @param target The value to compare against
+         * @param mask A bit mask
+         * @return This builder
+         */
+        @NonNull
+        public Builder addFlag(@NonNull String name, int target, int mask) {
+            mFlags.add(new Flag(name, target, mask));
+            return this;
+        }
+
+        /**
+         * Clear the builder, allowing for recycling.
+         */
+        public void clear() {
+            mFlags.clear();
+        }
+
+        /**
+         * Build a new {@link IntFlagMapping} from this builder.
+         *
+         * @return A new mapping
+         */
+        @NonNull
+        public IntFlagMapping build() {
+            return new IntFlagMapping(this);
+        }
+    }
+
+    /**
+     * Inner class that holds the name, mask, and target value of a flag
+     */
+    private static final class Flag {
+        @NonNull private final String mName;
+        private final int mTarget;
+        private final int mMask;
+
+        private Flag(@NonNull String name, int target, int mask) {
+            mName = name;
+            mTarget = target;
+            mMask = mask;
+        }
+
+        /**
+         * Compare the supplied property value against the mask and taget.
+         *
+         * @param value The value to check
+         * @return True if this flag is enabled
+         */
+        private boolean isEnabledFor(int value) {
+            return (value & mMask) == mTarget;
+        }
+    }
+}
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 35550bd..5fb291b 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -16,102 +16,160 @@
 
 package android.view.inspector;
 
+import android.annotation.AttrRes;
 import android.annotation.NonNull;
 
 /**
  * An interface for mapping the string names of inspectable properties to integer identifiers.
  *
- * This interface is consumed by {@link InspectionHelper#mapProperties(PropertyMapper)}.
+ * This interface is consumed by {@link InspectionCompanion#mapProperties(PropertyMapper)}.
  *
  * Mapping properties to IDs enables quick comparisons against shadow copies of inspectable
  * objects without performing a large number of string comparisons.
  *
- * @see InspectionHelper#mapProperties(PropertyMapper)
- * @hide
+ * @see InspectionCompanion#mapProperties(PropertyMapper)
  */
 public interface PropertyMapper {
     /**
      * Map a string name to an integer ID for a primitive boolean property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapBoolean(@NonNull String name);
+    int mapBoolean(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive byte property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapByte(@NonNull String name);
+    int mapByte(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive char property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapChar(@NonNull String name);
+    int mapChar(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive double property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapDouble(@NonNull String name);
+    int mapDouble(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive float property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapFloat(@NonNull String name);
+    int mapFloat(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive int property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapInt(@NonNull String name);
+    int mapInt(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive long property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapLong(@NonNull String name);
+    int mapLong(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for a primitive short property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapShort(@NonNull String name);
+    int mapShort(@NonNull String name, @AttrRes int attributeId);
 
     /**
      * Map a string name to an integer ID for an object property.
      *
      * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
      * @return An integer ID for the property
      * @throws PropertyConflictException If the property name is already mapped as another type.
      */
-    int mapObject(@NonNull String name);
+    int mapObject(@NonNull String name, @AttrRes int attributeId);
 
     /**
+     * Map a string name to an integer ID for a color property.
+     *
+     * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     * @see android.graphics.Color
+     */
+    int mapColor(@NonNull String name, @AttrRes int attributeId);
+
+    /**
+     * Map a string name to an integer ID for a gravity property.
+     *
+     * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     * @see android.view.Gravity
+     */
+    int mapGravity(@NonNull String name, @AttrRes int attributeId);
+
+    /**
+     * Map a string name to an integer ID for an enumeration packed into an int property.
+     *
+     * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
+     * @param mapping A mapping from int to String
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapIntEnum(
+            @NonNull String name,
+            @AttrRes int attributeId,
+            @NonNull IntEnumMapping mapping);
+
+    /**
+     * Map a string name to an integer ID for a flag set packed into an int property.
+     *
+     * @param name The name of the property
+     * @param attributeId If the property is from an XML attribute, the resource ID of the property
+     * @param mapping A mapping from int to an array of strings
+     * @return An integer ID for the property
+     * @throws PropertyConflictException If the property name is already mapped as another type.
+     */
+    int mapIntFlag(
+            @NonNull String name,
+            @AttrRes int attributeId,
+            @NonNull IntFlagMapping mapping);
+    /**
      * Thrown from a map method if a property name is already mapped as different type.
      */
     class PropertyConflictException extends RuntimeException {
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index df81c10..fd83e8d 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -16,19 +16,21 @@
 
 package android.view.inspector;
 
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Color;
 
 /**
  * An interface for reading the properties of an inspectable object.
  *
- * Used as the parameter for {@link InspectionHelper#readProperties(Object, PropertyReader)}.
+ * Used as the parameter for {@link InspectionCompanion#readProperties(Object, PropertyReader)}.
  * It has separate methods for all primitive types to avoid autoboxing overhead if a concrete
  * implementation is able to work with primitives. Implementations should be prepared to accept
  * {null} as the value of {@link PropertyReader#readObject(int, Object)}.
  *
- * @see InspectionHelper#readProperties(Object, PropertyReader)
- * @hide
+ * @see InspectionCompanion#readProperties(Object, PropertyReader)
  */
 public interface PropertyReader {
     /**
@@ -115,6 +117,60 @@
     void readObject(int id, @Nullable Object value);
 
     /**
+     * Read a color packed into a {@link ColorInt} as a property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+     */
+    void readColor(int id, @ColorInt int value);
+
+    /**
+     * Read a color packed into a {@link ColorLong} as a property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+     */
+    void readColor(int id, @ColorLong long value);
+
+    /**
+     * Read a {@link Color} object as a property.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
+     */
+    void readColor(int id, @Nullable Color value);
+
+    /**
+     * Read {@link android.view.Gravity} packed into an primitive {int}.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as a gravity property
+     */
+    void readGravity(int id, int value);
+
+    /**
+     * Read an enumeration packed into a primitive {int}.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+     */
+    void readIntEnum(int id, int value);
+
+    /**
+     * Read a flag packed into a primitive {int}.
+     *
+     * @param id Identifier of the property from a {@link PropertyMapper}
+     * @param value Value of the property
+     * @throws PropertyTypeMismatchException If the property ID is not mapped as an object
+     */
+    void readIntFlag(int id, int value);
+
+    /**
      * Thrown if a client calls a typed read method for a property of a different type.
      */
     class PropertyTypeMismatchException extends RuntimeException {
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
index befcb55..f636281 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -163,7 +163,7 @@
      * Gets optional flags associated with the event.
      *
      * @return either {@code 0} or
-     * {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
+     * {@link android.view.intelligence.ContentCaptureManager#FLAG_USER_INPUT}.
      */
     public int getFlags() {
         return mFlags;
diff --git a/core/java/android/view/intelligence/ContentCaptureManager.java b/core/java/android/view/intelligence/ContentCaptureManager.java
new file mode 100644
index 0000000..45518d5
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureManager.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2018 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.view.intelligence;
+
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
+import android.view.intelligence.ContentCaptureEvent.EventType;
+
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/*
+ * NOTE: all methods in this class should return right away, or do the real work in a handler
+ * thread.
+ *
+ * Hence, the only field that must be thread-safe is mEnabled, which is called at the beginning
+ * of every method.
+ */
+/**
+ * TODO(b/111276913): add javadocs / implement
+ */
+@SystemService(Context.CONTENT_CAPTURE_MANAGER_SERVICE)
+public final class ContentCaptureManager {
+
+    private static final String TAG = "ContentCaptureManager";
+
+    // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
+    private static final boolean VERBOSE = false;
+    private static final boolean DEBUG = true; // STOPSHIP if not set to false
+
+    /**
+     * Used to indicate that a text change was caused by user input (for example, through IME).
+     */
+    //TODO(b/111276913): link to notifyTextChanged() method once available
+    public static final int FLAG_USER_INPUT = 0x1;
+
+    /**
+     * Initial state, when there is no session.
+     *
+     * @hide
+     */
+    public static final int STATE_UNKNOWN = 0;
+
+    /**
+     * Service's startSession() was called, but server didn't confirm it was created yet.
+     *
+     * @hide
+     */
+    public static final int STATE_WAITING_FOR_SERVER = 1;
+
+    /**
+     * Session is active.
+     *
+     * @hide
+     */
+    public static final int STATE_ACTIVE = 2;
+
+    /**
+     * Session is disabled.
+     *
+     * @hide
+     */
+    public static final int STATE_DISABLED = 3;
+
+    private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
+
+    /**
+     * Maximum number of events that are buffered before sent to the app.
+     */
+    // TODO(b/111276913): use settings
+    private static final int MAX_BUFFER_SIZE = 100;
+
+    @NonNull
+    private final AtomicBoolean mDisabled = new AtomicBoolean();
+
+    @NonNull
+    private final Context mContext;
+
+    @Nullable
+    private final IIntelligenceManager mService;
+
+    @Nullable
+    private InteractionSessionId mId;
+
+    private int mState = STATE_UNKNOWN;
+
+    @Nullable
+    private IBinder mApplicationToken;
+
+    @Nullable
+    private ComponentName mComponentName;
+
+    /**
+     * List of events held to be sent as a batch.
+     */
+    @Nullable
+    private ArrayList<ContentCaptureEvent> mEvents;
+
+    // TODO(b/111276913): use UI Thread directly (as calls are one-way) or a shared thread / handler
+    // held at the Application level
+    private final Handler mHandler;
+
+    /** @hide */
+    public ContentCaptureManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
+        mContext = Preconditions.checkNotNull(context, "context cannot be null");
+        if (VERBOSE) {
+            Log.v(TAG, "Constructor for " + context.getPackageName());
+        }
+        mService = service;
+        // TODO(b/111276913): use an existing bg thread instead...
+        final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
+        bgThread.start();
+        mHandler = Handler.createAsync(bgThread.getLooper());
+    }
+
+    /** @hide */
+    public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
+        if (!isContentCaptureEnabled()) return;
+
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleStartSession, this,
+                token, componentName));
+    }
+
+    private void handleStartSession(@NonNull IBinder token, @NonNull ComponentName componentName) {
+        if (mState != STATE_UNKNOWN) {
+            // TODO(b/111276913): revisit this scenario
+            Log.w(TAG, "ignoring handleStartSession(" + token + ") while on state "
+                    + getStateAsString(mState));
+            return;
+        }
+        mState = STATE_WAITING_FOR_SERVER;
+        mId = new InteractionSessionId();
+        mApplicationToken = token;
+        mComponentName = componentName;
+
+        if (VERBOSE) {
+            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
+                    + getActivityDebugName() + ", id=" + mId);
+        }
+        final int flags = 0; // TODO(b/111276913): get proper flags
+
+        try {
+            mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+                    mId, flags, new IResultReceiver.Stub() {
+                        @Override
+                        public void send(int resultCode, Bundle resultData) {
+                            handleSessionStarted(resultCode);
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error starting session for " + componentName.flattenToShortString() + ": "
+                    + e);
+        }
+    }
+
+    private void handleSessionStarted(int resultCode) {
+        mState = resultCode;
+        mDisabled.set(mState == STATE_DISABLED);
+        if (VERBOSE) {
+            Log.v(TAG, "onActivityStarted() result: code=" + resultCode + ", id=" + mId
+                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get());
+        }
+    }
+
+    private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
+        if (mEvents == null) {
+            if (VERBOSE) {
+                Log.v(TAG, "Creating buffer for " + MAX_BUFFER_SIZE + " events");
+            }
+            mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
+        }
+        mEvents.add(event);
+        final int numberEvents = mEvents.size();
+        if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
+            // Buffering events, return right away...
+            return;
+        }
+
+        if (mState != STATE_ACTIVE) {
+            // Callback from startSession hasn't been called yet - typically happens on system
+            // apps that are started before the system service
+            // TODO(b/111276913): try to ignore session while system is not ready / boot
+            // not complete instead. Similarly, the manager service should return right away
+            // when the user does not have a service set
+            if (VERBOSE) {
+                Log.v(TAG, "Closing session for " + getActivityDebugName()
+                        + " after " + numberEvents + " delayed events and state "
+                        + getStateAsString(mState));
+            }
+            handleResetState();
+            // TODO(b/111276913): blacklist activity / use special flag to indicate that
+            // when it's launched again
+            return;
+        }
+
+        if (mId == null) {
+            // Sanity check - should not happen
+            Log.wtf(TAG, "null session id for " + getActivityDebugName());
+            return;
+        }
+
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
+            }
+            mService.sendEvents(mContext.getUserId(), mId, mEvents);
+            // TODO(b/111276913): decide whether we should clear or set it to null, as each has
+            // its own advantages: clearing will save extra allocations while the session is
+            // active, while setting to null would save memory if there's no more event coming.
+            mEvents.clear();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error sending " + numberEvents + " for " + getActivityDebugName()
+                    + ": " + e);
+        }
+    }
+
+    /**
+     * Used for intermediate events (i.e, other than created and destroyed).
+     *
+     * @hide
+     */
+    public void onActivityLifecycleEvent(@EventType int type) {
+        if (!isContentCaptureEnabled()) return;
+        if (VERBOSE) {
+            Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugName()
+                    + ": " + ContentCaptureEvent.getTypeAsString(type));
+        }
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+                new ContentCaptureEvent(type), /* forceFlush= */ true));
+    }
+
+    /** @hide */
+    public void onActivityDestroyed() {
+        if (!isContentCaptureEnabled()) return;
+
+        //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+        // id) and send it to the cache of batched commands
+        if (VERBOSE) {
+            Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
+                    + ", mId=" + mId);
+        }
+
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleFinishSession, this));
+    }
+
+    private void handleFinishSession() {
+        //TODO(b/111276913): right now both the ContentEvents and lifecycle sessions are sent
+        // to system_server, so it's ok to call both in sequence here. But once we split
+        // them so the events are sent directly to the service, we need to make sure they're
+        // sent in order.
+        try {
+            if (DEBUG) {
+                Log.d(TAG, "Finishing session " + mId + " with "
+                        + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
+                        + getActivityDebugName());
+            }
+
+            mService.finishSession(mContext.getUserId(), mId, mEvents);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error finishing session " + mId + " for " + getActivityDebugName()
+                    + ": " + e);
+        } finally {
+            handleResetState();
+        }
+    }
+
+    private void handleResetState() {
+        mState = STATE_UNKNOWN;
+        mId = null;
+        mApplicationToken = null;
+        mComponentName = null;
+        mEvents = null;
+    }
+
+    /**
+     * Notifies the Intelligence Service that a node has been added to the view structure.
+     *
+     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+     * automatically by the Android System for views that return {@code true} on
+     * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
+     *
+     * @param node node that has been added.
+     */
+    public void notifyViewAppeared(@NonNull ViewStructure node) {
+        Preconditions.checkNotNull(node);
+        if (!isContentCaptureEnabled()) return;
+
+        if (!(node instanceof ViewNode.ViewStructureImpl)) {
+            throw new IllegalArgumentException("Invalid node class: " + node.getClass());
+        }
+
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+                        .setViewNode(((ViewNode.ViewStructureImpl) node).mNode),
+                        /* forceFlush= */ false));
+    }
+
+    /**
+     * Notifies the Intelligence Service that a node has been removed from the view structure.
+     *
+     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+     * automatically by the Android System for standard views.
+     *
+     * @param id id of the node that has been removed.
+     */
+    public void notifyViewDisappeared(@NonNull AutofillId id) {
+        Preconditions.checkNotNull(id);
+        if (!isContentCaptureEnabled()) return;
+
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id),
+                        /* forceFlush= */ false));
+    }
+
+    /**
+     * Notifies the Intelligence Service that the value of a text node has been changed.
+     *
+     * @param id of the node.
+     * @param text new text.
+     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
+     * changed by the user (for example, through the keyboard).
+     */
+    public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+            int flags) {
+        Preconditions.checkNotNull(id);
+
+        if (!isContentCaptureEnabled()) return;
+
+        mHandler.sendMessage(obtainMessage(ContentCaptureManager::handleSendEvent, this,
+                new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+                        .setText(text), /* forceFlush= */ false));
+    }
+
+    /**
+     * Creates a {@link ViewStructure} for a "standard" view.
+     *
+     * @hide
+     */
+    @NonNull
+    public ViewStructure newViewStructure(@NonNull View view) {
+        return new ViewNode.ViewStructureImpl(view);
+    }
+
+    /**
+     * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
+     * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+     *
+     * @param parentId id of the virtual view parent (it can be obtained by calling
+     * {@link ViewStructure#getAutofillId()} on the parent).
+     * @param virtualId id of the virtual child, relative to the parent.
+     *
+     * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+     */
+    @NonNull
+    public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+        return new ViewNode.ViewStructureImpl(parentId, virtualId);
+    }
+
+    /**
+     * Returns the component name of the system service that is consuming the captured events for
+     * the current user.
+     */
+    @Nullable
+    public ComponentName getServiceComponentName() {
+        //TODO(b/111276913): implement
+        return null;
+    }
+
+    /**
+     * Checks whether content capture is enabled for this activity.
+     */
+    public boolean isContentCaptureEnabled() {
+        return mService != null && !mDisabled.get();
+    }
+
+    /**
+     * Called by apps to explicitly enable or disable content capture.
+     *
+     * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
+     * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
+     */
+    public void setContentCaptureEnabled(boolean enabled) {
+        //TODO(b/111276913): implement
+    }
+
+    /** @hide */
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.println("IntelligenceManager");
+        final String prefix2 = prefix + "  ";
+        pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+        if (mService != null) {
+            pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+        }
+        pw.print(prefix2); pw.print("mDisabled: "); pw.println(mDisabled.get());
+        pw.print(prefix2); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
+        if (mId != null) {
+            pw.print(prefix2); pw.print("id: "); pw.println(mId);
+        }
+        pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+        pw.print(getStateAsString(mState)); pw.println(")");
+        if (mApplicationToken != null) {
+            pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
+        }
+        if (mComponentName != null) {
+            pw.print(prefix2); pw.print("component name: ");
+            pw.println(mComponentName.flattenToShortString());
+        }
+        if (mEvents != null) {
+            final int numberEvents = mEvents.size();
+            pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
+            pw.print('/'); pw.println(MAX_BUFFER_SIZE);
+            if (VERBOSE && numberEvents > 0) {
+                final String prefix3 = prefix2 + "  ";
+                for (int i = 0; i < numberEvents; i++) {
+                    final ContentCaptureEvent event = mEvents.get(i);
+                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
+                    pw.println();
+                }
+            }
+        }
+    }
+
+    /**
+     * Gets a string that can be used to identify the activity on logging statements.
+     */
+    private String getActivityDebugName() {
+        return mComponentName == null ? mContext.getPackageName()
+                : mComponentName.flattenToShortString();
+    }
+
+    @NonNull
+    private static String getStateAsString(int state) {
+        switch (state) {
+            case STATE_UNKNOWN:
+                return "UNKNOWN";
+            case STATE_WAITING_FOR_SERVER:
+                return "WAITING_FOR_SERVER";
+            case STATE_ACTIVE:
+                return "ACTIVE";
+            case STATE_DISABLED:
+                return "DISABLED";
+            default:
+                return "INVALID:" + state;
+        }
+    }
+}
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index 2f128de..882fb26 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -28,6 +28,7 @@
 /**
  * {@hide}
  */
+// TODO(b/111276913): rename once the final name is defined
 oneway interface IIntelligenceManager {
     /**
       * Starts a session, sending the "remote" sessionId to the receiver.
@@ -38,7 +39,8 @@
     /**
       * Finishes a session.
       */
-    void finishSession(int userId, in InteractionSessionId sessionId);
+    void finishSession(int userId, in InteractionSessionId sessionId,
+                       in List<ContentCaptureEvent> events);
 
     /**
       * Sends a batch of events
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
deleted file mode 100644
index 755c54c..0000000
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ /dev/null
@@ -1,554 +0,0 @@
-/*
- * Copyright (C) 2018 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.view.intelligence;
-
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.intelligence.InteractionSessionId;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewStructure;
-import android.view.autofill.AutofillId;
-import android.view.intelligence.ContentCaptureEvent.EventType;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.util.Preconditions;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * TODO(b/111276913): add javadocs / implement
- */
-@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
-public final class IntelligenceManager {
-
-    private static final String TAG = "IntelligenceManager";
-
-    // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
-    private static final boolean VERBOSE = false;
-    private static final boolean DEBUG = true; // STOPSHIP if not set to false
-
-    /**
-     * Used to indicate that a text change was caused by user input (for example, through IME).
-     */
-    //TODO(b/111276913): link to notifyTextChanged() method once available
-    public static final int FLAG_USER_INPUT = 0x1;
-
-    /**
-     * Initial state, when there is no session.
-     *
-     * @hide
-     */
-    public static final int STATE_UNKNOWN = 0;
-
-    /**
-     * Service's startSession() was called, but server didn't confirm it was created yet.
-     *
-     * @hide
-     */
-    public static final int STATE_WAITING_FOR_SERVER = 1;
-
-    /**
-     * Session is active.
-     *
-     * @hide
-     */
-    public static final int STATE_ACTIVE = 2;
-
-    /**
-     * Session is disabled.
-     *
-     * @hide
-     */
-    public static final int STATE_DISABLED = 3;
-
-    private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
-
-    /**
-     * Maximum number of events that are delayed for an app.
-     *
-     * <p>If the session is not started after the limit is reached, it's discarded.
-     */
-    private static final int MAX_DELAYED_SIZE = 20;
-
-    private final Context mContext;
-
-    @Nullable
-    private final IIntelligenceManager mService;
-
-    private final Object mLock = new Object();
-
-    @Nullable
-    @GuardedBy("mLock")
-    private InteractionSessionId mId;
-
-    @GuardedBy("mLock")
-    private int mState = STATE_UNKNOWN;
-
-    @GuardedBy("mLock")
-    private IBinder mApplicationToken;
-
-    // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
-    // AutofillClient
-    @GuardedBy("mLock")
-    private ComponentName mComponentName;
-
-    // TODO(b/111276913): create using maximum batch size as capacity
-    /**
-     * List of events held to be sent as a batch.
-     */
-    @GuardedBy("mLock")
-    private final ArrayList<ContentCaptureEvent> mEvents = new ArrayList<>();
-
-    private final Handler mHandler;
-
-    /** @hide */
-    public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
-        mContext = Preconditions.checkNotNull(context, "context cannot be null");
-        mService = service;
-
-        // TODO(b/111276913): use an existing bg thread instead...
-        final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
-        bgThread.start();
-        mHandler = Handler.createAsync(bgThread.getLooper());
-    }
-
-    /** @hide */
-    public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
-        if (!isContentCaptureEnabled()) return;
-
-        synchronized (mLock) {
-            if (mState != STATE_UNKNOWN) {
-                // TODO(b/111276913): revisit this scenario
-                Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
-                        + getStateAsString(mState));
-                return;
-            }
-            mState = STATE_WAITING_FOR_SERVER;
-            mId = new InteractionSessionId();
-            mApplicationToken = token;
-            mComponentName = componentName;
-
-            if (VERBOSE) {
-                Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
-                        + getActivityDebugNameLocked() + ", id=" + mId);
-            }
-            final int flags = 0; // TODO(b/111276913): get proper flags
-
-            try {
-                mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
-                        mId, flags, new IResultReceiver.Stub() {
-                            @Override
-                            public void send(int resultCode, Bundle resultData)
-                                    throws RemoteException {
-                                synchronized (mLock) {
-                                    mState = resultCode;
-                                    if (VERBOSE) {
-                                        Log.v(TAG, "onActivityStarted() result: code=" + resultCode
-                                                + ", id=" + mId
-                                                + ", state=" + getStateAsString(mState));
-                                    }
-                                }
-                            }
-                        });
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
-    // calling right away
-    private void sendEvent(@NonNull ContentCaptureEvent event) {
-        mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this, event));
-    }
-
-    private void handleSendEvent(@NonNull ContentCaptureEvent event) {
-
-        //TODO(b/111276913): make a copy and don't use lock
-        synchronized (mLock) {
-            mEvents.add(event);
-            final int numberEvents = mEvents.size();
-            if (mState != STATE_ACTIVE) {
-                if (numberEvents >= MAX_DELAYED_SIZE) {
-                    // Typically happens on system apps that are started before the system service
-                    // is ready (like com.android.settings/.FallbackHome)
-                    //TODO(b/111276913): try to ignore session while system is not ready / boot
-                    // not complete instead. Similarly, the manager service should return right away
-                    // when the user does not have a service set
-                    if (VERBOSE) {
-                        Log.v(TAG, "Closing session for " + getActivityDebugNameLocked()
-                                + " after " + numberEvents + " delayed events and state "
-                                + getStateAsString(mState));
-                    }
-                    // TODO(b/111276913): blacklist activity / use special flag to indicate that
-                    // when it's launched again
-                    resetStateLocked();
-                    return;
-                }
-
-                if (VERBOSE) {
-                    Log.v(TAG, "Delaying " + numberEvents + " events for "
-                            + getActivityDebugNameLocked() + " while on state "
-                            + getStateAsString(mState));
-                }
-                return;
-            }
-
-            if (mId == null) {
-                // Sanity check - should not happen
-                Log.wtf(TAG, "null session id for " + mComponentName);
-                return;
-            }
-
-            //TODO(b/111276913): right now we're sending sending right away (unless not ready), but
-            // we should hold the events and flush later.
-            try {
-                if (DEBUG) {
-                    Log.d(TAG, "Sending " + numberEvents + " event(s) for "
-                            + getActivityDebugNameLocked());
-                }
-                mService.sendEvents(mContext.getUserId(), mId, mEvents);
-                mEvents.clear();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
-     * Used for intermediate events (i.e, other than created and destroyed).
-     *
-     * @hide
-     */
-    public void onActivityLifecycleEvent(@EventType int type) {
-        if (!isContentCaptureEnabled()) return;
-        if (VERBOSE) {
-            Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
-                    + ": " + ContentCaptureEvent.getTypeAsString(type));
-        }
-        sendEvent(new ContentCaptureEvent(type));
-    }
-
-    /** @hide */
-    public void onActivityDestroyed() {
-        if (!isContentCaptureEnabled()) return;
-
-        synchronized (mLock) {
-            //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
-            // id) and send it to the cache of batched commands
-
-            if (VERBOSE) {
-                Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
-                        + ", mId=" + mId);
-            }
-
-            try {
-                mService.finishSession(mContext.getUserId(), mId);
-                resetStateLocked();
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void resetStateLocked() {
-        mState = STATE_UNKNOWN;
-        mId = null;
-        mApplicationToken = null;
-        mComponentName = null;
-        mEvents.clear();
-    }
-
-    /**
-     * Notifies the Intelligence Service that a node has been added to the view structure.
-     *
-     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
-     * automatically by the Android System for views that return {@code true} on
-     * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
-     *
-     * @param node node that has been added.
-     */
-    public void notifyViewAppeared(@NonNull ViewStructure node) {
-        Preconditions.checkNotNull(node);
-        if (!isContentCaptureEnabled()) return;
-
-        if (!(node instanceof ViewNode.ViewStructureImpl)) {
-            throw new IllegalArgumentException("Invalid node class: " + node.getClass());
-        }
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
-                .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
-    }
-
-    /**
-     * Notifies the Intelligence Service that a node has been removed from the view structure.
-     *
-     * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
-     * automatically by the Android System for standard views.
-     *
-     * @param id id of the node that has been removed.
-     */
-    public void notifyViewDisappeared(@NonNull AutofillId id) {
-        Preconditions.checkNotNull(id);
-        if (!isContentCaptureEnabled()) return;
-
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
-    }
-
-    /**
-     * Notifies the Intelligence Service that the value of a text node has been changed.
-     *
-     * @param id of the node.
-     * @param text new text.
-     * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
-     * changed by the user (for example, through the keyboard).
-     */
-    public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
-            int flags) {
-        Preconditions.checkNotNull(id);
-        if (!isContentCaptureEnabled()) return;
-
-        sendEvent(new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
-                .setText(text));
-    }
-
-    /**
-     * Creates a {@link ViewStructure} for a "standard" view.
-     *
-     * @hide
-     */
-    @NonNull
-    public ViewStructure newViewStructure(@NonNull View view) {
-        return new ViewNode.ViewStructureImpl(view);
-    }
-
-    /**
-     * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
-     * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
-     *
-     * @param parentId id of the virtual view parent (it can be obtained by calling
-     * {@link ViewStructure#getAutofillId()} on the parent).
-     * @param virtualId id of the virtual child, relative to the parent.
-     *
-     * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
-     */
-    @NonNull
-    public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
-        return new ViewNode.ViewStructureImpl(parentId, virtualId);
-    }
-
-    /**
-     * Returns the component name of the {@code android.service.intelligence.IntelligenceService}
-     * that is enabled for the current user.
-     */
-    @Nullable
-    public ComponentName getIntelligenceServiceComponentName() {
-        //TODO(b/111276913): implement
-        return null;
-    }
-
-    /**
-     * Checks whether content capture is enabled for this activity.
-     */
-    public boolean isContentCaptureEnabled() {
-        //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
-        // service, or if service is not set
-        // (and probably renamign to isEnabledLocked()
-        return mService != null && mState != STATE_DISABLED;
-    }
-
-    /**
-     * Called by apps to explicitly enable or disable content capture.
-     *
-     * <p><b>Note: </b> this call is not persisted accross reboots, so apps should typically call
-     * it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
-     */
-    public void setContentCaptureEnabled(boolean enabled) {
-        //TODO(b/111276913): implement
-    }
-
-    /**
-     * Called by the the service {@link android.service.intelligence.IntelligenceService}
-     * to define whether content capture should be enabled for activities with such
-     * {@link android.content.ComponentName}.
-     *
-     * <p>Useful to blacklist a particular activity.
-     *
-     * @throws UnsupportedOperationException if not called by the UID that owns the
-     * {@link android.service.intelligence.IntelligenceService} associated with the
-     * current user.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void setActivityContentCaptureEnabled(@NonNull ComponentName activity,
-            boolean enabled) {
-        //TODO(b/111276913): implement
-    }
-
-    /**
-     * Called by the the service {@link android.service.intelligence.IntelligenceService}
-     * to explicitly limit content capture to the given packages and activities.
-     *
-     * <p>When the whitelist is set, it overrides the values passed to
-     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}
-     * and {@link #setPackageContentCaptureEnabled(String, boolean)}.
-     *
-     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
-     *
-     * <p>Useful when the service wants to restrict content capture to a category of apps, like
-     * chat apps. For example, if the service wants to support view captures on all activities of
-     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
-     * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
-     * Arrays.asList(new ComponentName("ChatApp2", "act1"),
-     * new ComponentName("ChatApp2", "act2")));}
-     *
-     * @throws UnsupportedOperationException if not called by the UID that owns the
-     * {@link android.service.intelligence.IntelligenceService} associated with the
-     * current user.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void setContentCaptureWhitelist(@Nullable List<String> packages,
-            @Nullable List<ComponentName> activities) {
-        //TODO(b/111276913): implement
-    }
-
-    /**
-     * Called by the the service {@link android.service.intelligence.IntelligenceService}
-     * to define whether content capture should be enabled for activities of the app with such
-     * {@code packageName}.
-     *
-     * <p>Useful to blacklist any activity from a particular app.
-     *
-     * @throws UnsupportedOperationException if not called by the UID that owns the
-     * {@link android.service.intelligence.IntelligenceService} associated with the
-     * current user.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void setPackageContentCaptureEnabled(@NonNull String packageName, boolean enabled) {
-        //TODO(b/111276913): implement
-    }
-
-    /**
-     * Gets the activities where content capture was disabled by
-     * {@link #setActivityContentCaptureEnabled(ComponentName, boolean)}.
-     *
-     * @throws UnsupportedOperationException if not called by the UID that owns the
-     * {@link android.service.intelligence.IntelligenceService} associated with the
-     * current user.
-     *
-     * @hide
-     */
-    @SystemApi
-    @NonNull
-    public Set<ComponentName> getContentCaptureDisabledActivities() {
-        //TODO(b/111276913): implement
-        return null;
-    }
-
-    /**
-     * Gets the apps where content capture was disabled by
-     * {@link #setPackageContentCaptureEnabled(String, boolean)}.
-     *
-     * @throws UnsupportedOperationException if not called by the UID that owns the
-     * {@link android.service.intelligence.IntelligenceService} associated with the
-     * current user.
-     *
-     * @hide
-     */
-    @SystemApi
-    @NonNull
-    public Set<String> getContentCaptureDisabledPackages() {
-        //TODO(b/111276913): implement
-        return null;
-    }
-
-    /** @hide */
-    public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.println("IntelligenceManager");
-        final String prefix2 = prefix + "  ";
-        synchronized (mLock) {
-            pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
-            pw.print(prefix2); pw.print("mService: "); pw.println(mService);
-            pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
-            pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
-            pw.print(prefix2); pw.print("id: "); pw.println(mId);
-            pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
-            pw.print(getStateAsString(mState)); pw.println(")");
-            pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
-            pw.print(prefix2); pw.print("component name: ");
-            pw.println(mComponentName == null ? "null" : mComponentName.flattenToShortString());
-            final int numberEvents = mEvents.size();
-            pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
-            if (numberEvents > 0) {
-                for (int i = 0; i < numberEvents; i++) {
-                    final ContentCaptureEvent event = mEvents.get(i);
-                    pw.println(i); pw.print(": "); event.dump(pw); pw.println();
-                }
-
-            }
-        }
-    }
-
-    /**
-     * Gets a string that can be used to identify the activity on logging statements.
-     */
-    @GuardedBy("mLock")
-    private String getActivityDebugNameLocked() {
-        return mComponentName == null ? mContext.getPackageName()
-                : mComponentName.flattenToShortString();
-    }
-
-    @NonNull
-    private static String getStateAsString(int state) {
-        switch (state) {
-            case STATE_UNKNOWN:
-                return "UNKNOWN";
-            case STATE_WAITING_FOR_SERVER:
-                return "WAITING_FOR_SERVER";
-            case STATE_ACTIVE:
-                return "ACTIVE";
-            case STATE_DISABLED:
-                return "DISABLED";
-            default:
-                return "INVALID:" + state;
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 524f709..a2536cb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -90,6 +90,11 @@
     String TYPE_DATE_TIME = "datetime";
     /** Flight number in IATA format. */
     String TYPE_FLIGHT_NUMBER = "flight";
+    /**
+     * Word that users may be interested to look up for meaning.
+     * @hide
+     */
+    String TYPE_DICTIONARY = "dictionary";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -103,6 +108,7 @@
             TYPE_DATE,
             TYPE_DATE_TIME,
             TYPE_FLIGHT_NUMBER,
+            TYPE_DICTIONARY
     })
     @interface EntityType {}
 
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 74678df..8e14dfd 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -42,6 +42,7 @@
 import android.provider.ContactsContract;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
@@ -57,6 +58,7 @@
 import java.time.Instant;
 import java.time.ZonedDateTime;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -638,7 +640,8 @@
     /**
      * Helper class to store the information from which RemoteActions are built.
      */
-    private static final class LabeledIntent {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    public static final class LabeledIntent {
 
         static final int DEFAULT_REQUEST_CODE = 0;
 
@@ -673,7 +676,8 @@
             return mDescription;
         }
 
-        Intent getIntent() {
+        @VisibleForTesting
+        public Intent getIntent() {
             return mIntent;
         }
 
@@ -717,7 +721,8 @@
     /**
      * Creates intents based on the classification type.
      */
-    static final class IntentFactory {
+    @VisibleForTesting
+    public static final class IntentFactory {
 
         private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5);
         private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1);
@@ -762,6 +767,9 @@
                 case TextClassifier.TYPE_FLIGHT_NUMBER:
                     actions = createForFlight(context, text);
                     break;
+                case TextClassifier.TYPE_DICTIONARY:
+                    actions = createForDictionary(context, text);
+                    break;
                 default:
                     actions = new ArrayList<>();
                     break;
@@ -924,5 +932,15 @@
                             .putExtra(Intent.EXTRA_TEXT, text),
                     text.hashCode()));
         }
+
+        @NonNull
+        private static List<LabeledIntent> createForDictionary(Context context, String text) {
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.define),
+                    context.getString(com.android.internal.R.string.define_desc),
+                    new Intent(Intent.ACTION_DEFINE)
+                            .putExtra(Intent.EXTRA_TEXT, text),
+                    text.hashCode()));
+        }
     }
 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 9ccd321..f781802 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Trace;
 import android.util.AttributeSet;
@@ -107,7 +108,7 @@
      */
     public static final int AUTO_FIT = -1;
 
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521080)
     private int mNumColumns = AUTO_FIT;
 
     @UnsupportedAppUsage
@@ -117,7 +118,7 @@
     @UnsupportedAppUsage
     private int mVerticalSpacing = 0;
     private int mStretchMode = STRETCH_COLUMN_WIDTH;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521079)
     private int mColumnWidth;
     @UnsupportedAppUsage
     private int mRequestedColumnWidth;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index f4c25c3..7d02757 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -837,7 +837,7 @@
             mSurfaceSession = new SurfaceSession(parentSurface);
             mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
                     .setFormat(PixelFormat.TRANSLUCENT)
-                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                    .setBufferSize(mSurfaceWidth, mSurfaceHeight)
                     .setName("magnifier surface")
                     .setFlags(SurfaceControl.HIDDEN)
                     .build();
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index a28cc40..157992a 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -18,6 +18,8 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Px;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
@@ -30,6 +32,7 @@
 import android.graphics.Paint.Align;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.text.InputFilter;
 import android.text.InputType;
@@ -88,6 +91,16 @@
  * of the current value. Tapping on the current value allows to type in a
  * desired value.
  * </li>
+ * <li>
+ * If the current theme is derived from {@link android.R.style#Theme_Material}
+ * the widget presents the current value as a scrolling vertical selector with
+ * the selected value in the center and the previous and following numbers above
+ * and below, separated by a divider. The value is changed by flinging vertically.
+ * The thickness of the divider can be changed by using the
+ * {@link android.R.attr#selectionDividerHeight} attribute and the color of the
+ * divider can be changed by using the
+ * {@link android.R.attr#colorControlNormal} attribute.
+ * </li>
  * </ul>
  * <p>
  * For an example of using this widget, see {@link android.widget.TimePicker}.
@@ -436,14 +449,14 @@
     /**
      * Divider for showing item to be selected while scrolling
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private final Drawable mSelectionDivider;
 
     /**
      * The height of the selection divider.
      */
-    @UnsupportedAppUsage
-    private final int mSelectionDividerHeight;
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
+    private int mSelectionDividerHeight;
 
     /**
      * The current scroll state of the number picker.
@@ -1556,6 +1569,24 @@
         return mSelectorIndexToStringCache.get(getValue());
     }
 
+    /**
+     * Set the height for the divider that separates the currently selected value from the others.
+     * @param height The height to be set
+     */
+    public void setSelectionDividerHeight(@IntRange(from = 0) @Px int height) {
+        mSelectionDividerHeight = height;
+        invalidate();
+    }
+
+    /**
+     * Retrieve the height for the divider that separates the currently selected value from the
+     * others.
+     * @return The height of the divider
+     */
+    public int getSelectionDividerHeight() {
+        return mSelectionDividerHeight;
+    }
+
     @Override
     protected float getTopFadingEdgeStrength() {
         return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index c0979fe..3b916d1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,6 +18,8 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
+import android.annotation.IntDef;
+import android.annotation.LayoutRes;
 import android.annotation.NonNull;
 import android.annotation.StyleRes;
 import android.annotation.UnsupportedAppUsage;
@@ -131,6 +133,12 @@
     static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
 
     /**
+     * The intent extra that contains {@code true} if inflating as dak text theme.
+     * @hide
+     */
+    static final String EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND = "remoteAdapterOnLightBackground";
+
+    /**
      * The intent extra that contains the bounds for all shared elements.
      */
     public static final String EXTRA_SHARED_ELEMENT_BOUNDS =
@@ -161,6 +169,37 @@
     private static final int LAYOUT_PARAM_ACTION_TAG = 19;
     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
+    private static final int SET_INT_TAG_TAG = 22;
+
+    /** @hide **/
+    @IntDef(flag = true, value = {
+            FLAG_REAPPLY_DISALLOWED,
+            FLAG_WIDGET_IS_COLLECTION_CHILD,
+            FLAG_USE_LIGHT_BACKGROUND_LAYOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApplyFlags {}
+    /**
+     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
+     * the layout in a way that isn't recoverable, since views are being removed.
+     * @hide
+     */
+    public static final int FLAG_REAPPLY_DISALLOWED = 1;
+    /**
+     * This flag indicates whether this RemoteViews object is being created from a
+     * RemoteViewsService for use as a child of a widget collection. This flag is used
+     * to determine whether or not certain features are available, in particular,
+     * setting on click extras and setting on click pending intents. The former is enabled,
+     * and the latter disabled when this flag is true.
+     * @hide
+     */
+    public static final int FLAG_WIDGET_IS_COLLECTION_CHILD = 2;
+    /**
+     * When this flag is set, the views is inflated with {@link #mLightBackgroundLayoutId} instead
+     * of {link #mLayoutId}
+     * @hide
+     */
+    public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
 
     /**
      * Application that hosts the remote views.
@@ -177,6 +216,11 @@
     private final int mLayoutId;
 
     /**
+     * The resource ID of the layout file in dark text mode. (Added to the parcel)
+     */
+    private int mLightBackgroundLayoutId = 0;
+
+    /**
      * An array of actions to perform on the view tree once it has been
      * inflated
      */
@@ -196,12 +240,6 @@
     private boolean mIsRoot = true;
 
     /**
-     * Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
-     * the layout in a way that isn't recoverable, since views are being removed.
-     */
-    private boolean mReapplyDisallowed;
-
-    /**
      * Constants to whether or not this RemoteViews is composed of a landscape and portrait
      * RemoteViews.
      */
@@ -217,14 +255,8 @@
     @UnsupportedAppUsage
     private RemoteViews mPortrait = null;
 
-    /**
-     * This flag indicates whether this RemoteViews object is being created from a
-     * RemoteViewsService for use as a child of a widget collection. This flag is used
-     * to determine whether or not certain features are available, in particular,
-     * setting on click extras and setting on click pending intents. The former is enabled,
-     * and the latter disabled when this flag is true.
-     */
-    private boolean mIsWidgetCollectionChild = false;
+    @ApplyFlags
+    private int mApplyFlags = 0;
 
     /** Class cookies of the Parcel this instance was read from. */
     private final Map<Class, Object> mClassCookies;
@@ -274,23 +306,29 @@
     }
 
     /**
+     * Sets an integer tag to the view.
+     *
+     * @hide
+     */
+    public void setIntTag(int viewId, int key, int tag) {
+        addAction(new SetIntTagAction(viewId, key, tag));
+    }
+
+    /**
      * Set that it is disallowed to reapply another remoteview with the same layout as this view.
      * This should be done if an action is destroying the view tree of the base layout.
      *
      * @hide
      */
-    public void setReapplyDisallowed() {
-        mReapplyDisallowed = true;
+    public void addFlags(@ApplyFlags int flags) {
+        mApplyFlags = mApplyFlags | flags;
     }
 
     /**
-     * @return Whether it is disallowed to reapply another remoteview with the same layout as this
-     * view. True if this remoteview has actions that destroyed view tree of the base layout.
-     *
      * @hide
      */
-    public boolean isReapplyDisallowed() {
-        return mReapplyDisallowed;
+    public boolean hasFlags(@ApplyFlags int flag) {
+        return (mApplyFlags & flag) == flag;
     }
 
     /**
@@ -758,7 +796,10 @@
             // Embed the AppWidget Id for use in RemoteViewsAdapter when connecting to the intent
             // RemoteViewsService
             AppWidgetHostView host = (AppWidgetHostView) rootParent;
-            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId());
+            intent.putExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, host.getAppWidgetId())
+                    .putExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND,
+                            hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT));
+
             if (target instanceof AbsListView) {
                 AbsListView v = (AbsListView) target;
                 v.setRemoteViewsAdapter(intent, isAsync);
@@ -819,7 +860,7 @@
                 // If the view is an AdapterView, setting a PendingIntent on click doesn't make
                 // much sense, do they mean to set a PendingIntent template for the
                 // AdapterView's children?
-                if (mIsWidgetCollectionChild) {
+                if (hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.w(LOG_TAG, "Cannot SetOnClickResponse for collection item "
                             + "(id: " + viewId + ")");
                     ApplicationInfo appInfo = root.getContext().getApplicationInfo();
@@ -833,7 +874,7 @@
                 }
                 target.setTagInternal(R.id.pending_intent_tag, mResponse.mPendingIntent);
             } else if (mResponse.mFillIntent != null) {
-                if (!mIsWidgetCollectionChild) {
+                if (!hasFlags(FLAG_WIDGET_IS_COLLECTION_CHILD)) {
                     Log.e(LOG_TAG, "The method setOnClickFillInIntent is available "
                             + "only from RemoteViewsFactory (ie. on collection items).");
                     return;
@@ -1535,6 +1576,7 @@
             viewId = parcel.readInt();
             mIndex = parcel.readInt();
             mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
+            mNestedViews.addFlags(mApplyFlags);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
@@ -2122,6 +2164,43 @@
         }
     }
 
+    private class SetIntTagAction extends Action {
+        private final int mViewId;
+        private final int mKey;
+        private final int mTag;
+
+        SetIntTagAction(int viewId, int key, int tag) {
+            mViewId = viewId;
+            mKey = key;
+            mTag = tag;
+        }
+
+        SetIntTagAction(Parcel parcel) {
+            mViewId = parcel.readInt();
+            mKey = parcel.readInt();
+            mTag = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mViewId);
+            dest.writeInt(mKey);
+            dest.writeInt(mTag);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final View target = root.findViewById(mViewId);
+            if (target == null) return;
+
+            target.setTagInternal(mKey, mTag);
+        }
+
+        @Override
+        public int getActionTag() {
+            return SET_INT_TAG_TAG;
+        }
+    }
+
     /**
      * Create a new RemoteViews object that will display the views contained
      * in the specified layout file.
@@ -2143,7 +2222,7 @@
      *
      * @hide
      */
-    public RemoteViews(String packageName, int userId, int layoutId) {
+    public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
         this(getApplicationInfo(packageName, userId), layoutId);
     }
 
@@ -2156,7 +2235,7 @@
      *
      * @hide
      */
-    protected RemoteViews(ApplicationInfo application, int layoutId) {
+    protected RemoteViews(ApplicationInfo application, @LayoutRes int layoutId) {
         mApplication = application;
         mLayoutId = layoutId;
         mBitmapCache = new BitmapCache();
@@ -2182,7 +2261,8 @@
             throw new RuntimeException("Both RemoteViews must share the same package and user");
         }
         mApplication = portrait.mApplication;
-        mLayoutId = portrait.getLayoutId();
+        mLayoutId = portrait.mLayoutId;
+        mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
 
         mLandscape = landscape;
         mPortrait = portrait;
@@ -2203,8 +2283,8 @@
         mApplication = src.mApplication;
         mIsRoot = src.mIsRoot;
         mLayoutId = src.mLayoutId;
-        mIsWidgetCollectionChild = src.mIsWidgetCollectionChild;
-        mReapplyDisallowed = src.mReapplyDisallowed;
+        mLightBackgroundLayoutId = src.mLightBackgroundLayoutId;
+        mApplyFlags = src.mApplyFlags;
         mClassCookies = src.mClassCookies;
 
         if (src.hasLandscapeAndPortraitLayouts()) {
@@ -2262,7 +2342,7 @@
             mApplication = parcel.readInt() == 0 ? info :
                     ApplicationInfo.CREATOR.createFromParcel(parcel);
             mLayoutId = parcel.readInt();
-            mIsWidgetCollectionChild = parcel.readInt() == 1;
+            mLightBackgroundLayoutId = parcel.readInt();
 
             readActionsFromParcel(parcel, depth);
         } else {
@@ -2271,9 +2351,10 @@
             mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth,
                     mClassCookies);
             mApplication = mPortrait.mApplication;
-            mLayoutId = mPortrait.getLayoutId();
+            mLayoutId = mPortrait.mLayoutId;
+            mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
         }
-        mReapplyDisallowed = parcel.readInt() == 0;
+        mApplyFlags = parcel.readInt();
     }
 
     private void readActionsFromParcel(Parcel parcel, int depth) {
@@ -2326,6 +2407,8 @@
                 return new OverrideTextColorsAction(parcel);
             case SET_RIPPLE_DRAWABLE_COLOR_TAG:
                 return new SetRippleDrawableColor(parcel);
+            case SET_INT_TAG_TAG:
+                return new SetIntTagAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -2360,19 +2443,8 @@
      * @return the layout id.
      */
     public int getLayoutId() {
-        return mLayoutId;
-    }
-
-    /*
-     * This flag indicates whether this RemoteViews object is being created from a
-     * RemoteViewsService for use as a child of a widget collection. This flag is used
-     * to determine whether or not certain features are available, in particular,
-     * setting on click extras and setting on click pending intents. The former is enabled,
-     * and the latter disabled when this flag is true.
-     */
-    @UnsupportedAppUsage
-    void setIsWidgetCollectionChild(boolean isWidgetCollectionChild) {
-        mIsWidgetCollectionChild = isWidgetCollectionChild;
+        return hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT) && (mLightBackgroundLayoutId != 0)
+                ? mLightBackgroundLayoutId : mLayoutId;
     }
 
     /**
@@ -3243,6 +3315,33 @@
         setInt(viewId, "setLabelFor", labeledId);
     }
 
+    /**
+     * Provides an alternate layout ID, which can be used to inflate this view. This layout will be
+     * used by the host when the widgets displayed on a light-background where foreground elements
+     * and text can safely draw using a dark color without any additional background protection.
+     */
+    public void setLightBackgroundLayoutId(@LayoutRes int layoutId) {
+        mLightBackgroundLayoutId = layoutId;
+    }
+
+    /**
+     * If this view supports dark text versions, creates a copy representing that version,
+     * otherwise returns itself.
+     * @hide
+     */
+    public RemoteViews getDarkTextViews() {
+        if (hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT)) {
+            return this;
+        }
+
+        try {
+            addFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
+            return new RemoteViews(this);
+        } finally {
+            mApplyFlags &= ~FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
+        }
+    }
+
     private RemoteViews getRemoteViewsToApply(Context context) {
         if (hasLandscapeAndPortraitLayouts()) {
             int orientation = context.getResources().getConfiguration().orientation;
@@ -3603,7 +3702,7 @@
                 mApplication.writeToParcel(dest, flags);
             }
             dest.writeInt(mLayoutId);
-            dest.writeInt(mIsWidgetCollectionChild ? 1 : 0);
+            dest.writeInt(mLightBackgroundLayoutId);
             writeActionsToParcel(dest);
         } else {
             dest.writeInt(MODE_HAS_LANDSCAPE_AND_PORTRAIT);
@@ -3616,7 +3715,7 @@
             // Both RemoteViews already share the same package and user
             mPortrait.writeToParcel(dest, flags | PARCELABLE_ELIDE_DUPLICATES);
         }
-        dest.writeInt(mReapplyDisallowed ? 1 : 0);
+        dest.writeInt(mApplyFlags);
     }
 
     private void writeActionsToParcel(Parcel parcel) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index d17c7c5..c5cd1a1 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -16,6 +16,9 @@
 
 package android.widget;
 
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
+import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
+
 import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.app.IServiceConnection;
@@ -97,6 +100,7 @@
     private final Context mContext;
     private final Intent mIntent;
     private final int mAppWidgetId;
+    private final boolean mOnLightBackground;
     private final Executor mAsyncViewLoadExecutor;
 
     private OnClickHandler mRemoteViewsOnClickHandler;
@@ -817,13 +821,13 @@
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
 
-        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+        mAppWidgetId = intent.getIntExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
         mRequestedViews = new RemoteViewsFrameLayoutRefSet();
+        mOnLightBackground = intent.getBooleanExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND, false);
 
         // Strip the previously injected app widget id from service intent
-        if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) {
-            intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID);
-        }
+        intent.removeExtra(EXTRA_REMOTEADAPTER_APPWIDGET_ID);
+        intent.removeExtra(EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND);
 
         // Initialize the worker thread
         mWorkerThread = new HandlerThread("RemoteViewsCache-loader");
@@ -1107,6 +1111,7 @@
             } else {
                 layout = new RemoteViewsFrameLayout(parent.getContext(), mCache);
                 layout.setExecutor(mAsyncViewLoadExecutor);
+                layout.setOnLightBackground(mOnLightBackground);
             }
 
             if (isInCache) {
diff --git a/core/java/android/widget/RemoteViewsListAdapter.java b/core/java/android/widget/RemoteViewsListAdapter.java
index e490458..b80fe48 100644
--- a/core/java/android/widget/RemoteViewsListAdapter.java
+++ b/core/java/android/widget/RemoteViewsListAdapter.java
@@ -85,7 +85,7 @@
     public View getView(int position, View convertView, ViewGroup parent) {
         if (position < getCount()) {
             RemoteViews rv = mRemoteViewsList.get(position);
-            rv.setIsWidgetCollectionChild(true);
+            rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
             View v;
             if (convertView != null && rv != null &&
                     convertView.getId() == rv.getLayoutId()) {
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 2827f63..214e5cc 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -163,7 +163,7 @@
             try {
                 rv = mFactory.getViewAt(position);
                 if (rv != null) {
-                    rv.setIsWidgetCollectionChild(true);
+                    rv.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
                 }
             } catch (Exception ex) {
                 Thread t = Thread.currentThread();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4ed9924f0d..085f8f1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -166,7 +166,7 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
@@ -6254,8 +6254,13 @@
         return mHint;
     }
 
-    @UnsupportedAppUsage
-    boolean isSingleLine() {
+    /**
+     * Returns if the text is constrained to a single horizontally scrolling line ignoring new
+     * line characters instead of letting it wrap onto multiple lines.
+     *
+     * @attr ref android.R.styleable#TextView_singleLine
+     */
+    public boolean isSingleLine() {
         return mSingleLine;
     }
 
@@ -10130,7 +10135,7 @@
     }
 
     /**
-     * Notify managers (such as {@link AutofillManager} and {@link IntelligenceManager}) that are
+     * Notify managers (such as {@link AutofillManager} and {@link ContentCaptureManager}) that are
      * interested on text changes.
      */
     private void notifyListeningManagersAfterTextChanged() {
@@ -10150,10 +10155,10 @@
 
         // ContentCapture
         if (isImportantForContentCapture() && isTextEditable()) {
-            final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
-            if (im != null && im.isContentCaptureEnabled()) {
+            final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
+            if (cm != null && cm.isContentCaptureEnabled()) {
                 // TODO(b/111276913): pass flags when edited by user / add CTS test
-                im.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+                cm.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
             }
         }
     }
@@ -12268,13 +12273,13 @@
     }
 
     /**
-     * Returns the current {@link TextDirectionHeuristic}.
-     *
-     * @return the current {@link TextDirectionHeuristic}.
-     * @hide
+     * Returns resolved {@link TextDirectionHeuristic} that will be used for text layout.
+     * The {@link TextDirectionHeuristic} that is used by TextView is only available after
+     * {@link #getTextDirection()} and {@link #getLayoutDirection()} is resolved. Therefore the
+     * return value may not be the same as the one TextView uses if the View's layout direction is
+     * not resolved or detached from parent root view.
      */
-    @UnsupportedAppUsage
-    protected TextDirectionHeuristic getTextDirectionHeuristic() {
+    public TextDirectionHeuristic getTextDirectionHeuristic() {
         if (hasPasswordTransformationMethod()) {
             // passwords fields should be LTR
             return TextDirectionHeuristics.LTR;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index e2b8f7d..5d08a25 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -309,9 +309,6 @@
         } else if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
-        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            prefer = RECOMMEND_INSTALL_EXTERNAL;
-            checkBoth = false;
         } else if (params.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
             prefer = RECOMMEND_INSTALL_INTERNAL;
             checkBoth = false;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 34e8ed4..a87bbf3 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Binder;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
@@ -52,17 +53,23 @@
     public static final boolean ENABLED_DEFAULT = false;
     public static final boolean DETAILED_TRACKING_DEFAULT = true;
     public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100;
+    public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000;
+
+    private static class OverflowBinder extends Binder {}
 
     private static final String TAG = "BinderCallsStats";
     private static final int CALL_SESSIONS_POOL_SIZE = 100;
     private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
     private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
+    private static final Class<? extends Binder> OVERFLOW_BINDER = OverflowBinder.class;
+    private static final int OVERFLOW_TRANSACTION_CODE = -1;
 
     // Whether to collect all the data: cpu + exceptions + reply/request sizes.
     private boolean mDetailedTracking = DETAILED_TRACKING_DEFAULT;
     // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out
     // of 100 requests.
     private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT;
+    private int mMaxBinderCallStatsCount = MAX_BINDER_CALL_STATS_COUNT_DEFAULT;
     @GuardedBy("mLock")
     private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
     @GuardedBy("mLock")
@@ -71,8 +78,11 @@
     private final Object mLock = new Object();
     private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
+    private long mCallStatsCount = 0;
+    private boolean mAddDebugEntries = false;
 
     private CachedDeviceState.Readonly mDeviceState;
+    private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
 
     /** Injector for {@link BinderCallsStats}. */
     public static class Injector {
@@ -86,7 +96,11 @@
     }
 
     public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.close();
+        }
         mDeviceState = deviceState;
+        mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
     }
 
     @Override
@@ -158,7 +172,13 @@
 
                 final CallStat callStat = uidEntry.getOrCreate(
                         callingUid, s.binderClass, s.transactionCode,
-                        mDeviceState.isScreenInteractive());
+                        mDeviceState.isScreenInteractive(),
+                        mCallStatsCount >= mMaxBinderCallStatsCount);
+                final boolean isNewCallStat = callStat.callCount == 0;
+                if (isNewCallStat) {
+                    mCallStatsCount++;
+                }
+
                 callStat.callCount++;
                 callStat.recordedCallCount++;
                 callStat.cpuTimeMicros += duration;
@@ -304,9 +324,30 @@
             exported.methodName = methodName;
         }
 
+        // Debug entries added to help validate the data.
+        if (mAddDebugEntries && mBatteryStopwatch != null) {
+            resultCallStats.add(createDebugEntry("start_time_millis", mStartTime));
+            resultCallStats.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            resultCallStats.add(
+                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
+        }
+
         return resultCallStats;
     }
 
+    private ExportedCallStat createDebugEntry(String variableName, long value) {
+        final int uid = Process.myUid();
+        final ExportedCallStat callStat = new ExportedCallStat();
+        callStat.className = "";
+        callStat.workSourceUid = uid;
+        callStat.callingUid = uid;
+        callStat.recordedCallCount = 1;
+        callStat.callCount = 1;
+        callStat.methodName = "__DEBUG_" + variableName;
+        callStat.maxReplySizeBytes = value;
+        return callStat;
+    }
+
     /** @hide */
     public ArrayMap<String, Integer> getExportedExceptionStats() {
         synchronized (mLock) {
@@ -328,6 +369,8 @@
         long totalCpuTime = 0;
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStartTime));
+        pw.print("On battery time (ms): ");
+        pw.println(mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0);
         pw.println("Sampling interval period: " + mPeriodicSamplingInterval);
         final List<UidEntry> entries = new ArrayList<>();
 
@@ -444,6 +487,28 @@
         }
     }
 
+    public void setAddDebugEntries(boolean addDebugEntries) {
+        mAddDebugEntries = addDebugEntries;
+    }
+
+    /**
+     * Sets the maximum number of items to track.
+     */
+    public void setMaxBinderCallStats(int maxKeys) {
+        if (maxKeys <= 0) {
+            Slog.w(TAG, "Ignored invalid max value (value must be positive): "
+                    + maxKeys);
+            return;
+        }
+
+        synchronized (mLock) {
+            if (maxKeys != mMaxBinderCallStatsCount) {
+                mMaxBinderCallStatsCount = maxKeys;
+                reset();
+            }
+        }
+    }
+
     public void setSamplingInterval(int samplingInterval) {
         if (samplingInterval <= 0) {
             Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
@@ -461,9 +526,13 @@
 
     public void reset() {
         synchronized (mLock) {
+            mCallStatsCount = 0;
             mUidEntries.clear();
             mExceptionCounts.clear();
             mStartTime = System.currentTimeMillis();
+            if (mBatteryStopwatch != null) {
+                mBatteryStopwatch.reset();
+            }
         }
     }
 
@@ -595,10 +664,21 @@
         }
 
         CallStat getOrCreate(int callingUid, Class<? extends Binder> binderClass,
-                int transactionCode, boolean screenInteractive) {
+                int transactionCode, boolean screenInteractive, boolean maxCallStatsReached) {
             CallStat mapCallStat = get(callingUid, binderClass, transactionCode, screenInteractive);
-            // Only create CallStat if it's a new entry, otherwise update existing instance
+            // Only create CallStat if it's a new entry, otherwise update existing instance.
             if (mapCallStat == null) {
+                if (maxCallStatsReached) {
+                    mapCallStat = get(callingUid, OVERFLOW_BINDER, OVERFLOW_TRANSACTION_CODE,
+                            screenInteractive);
+                    if (mapCallStat != null) {
+                        return mapCallStat;
+                    }
+
+                    binderClass = OVERFLOW_BINDER;
+                    transactionCode = OVERFLOW_TRANSACTION_CODE;
+                }
+
                 mapCallStat = new CallStat(callingUid, binderClass, transactionCode,
                         screenInteractive);
                 CallStatKey key = new CallStatKey();
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
index 8c90682..334cca3 100644
--- a/core/java/com/android/internal/os/CachedDeviceState.java
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -16,8 +16,14 @@
 
 package com.android.internal.os;
 
+
+import android.os.SystemClock;
+
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
+
 /**
  * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
  * the System Server telemetry services.
@@ -27,6 +33,9 @@
 public class CachedDeviceState {
     private volatile boolean mScreenInteractive;
     private volatile boolean mCharging;
+    private final Object mStopwatchesLock = new Object();
+    @GuardedBy("mStopwatchLock")
+    private final ArrayList<TimeInStateStopwatch> mOnBatteryStopwatches = new ArrayList<>();
 
     public CachedDeviceState() {
         mCharging = true;
@@ -44,7 +53,23 @@
     }
 
     public void setCharging(boolean charging) {
-        mCharging = charging;
+        if (mCharging != charging) {
+            mCharging = charging;
+            updateStopwatches(/* shouldStart= */ !charging);
+        }
+    }
+
+    private void updateStopwatches(boolean shouldStart) {
+        synchronized (mStopwatchesLock) {
+            final int size = mOnBatteryStopwatches.size();
+            for (int i = 0; i < size; i++) {
+                if (shouldStart) {
+                    mOnBatteryStopwatches.get(i).start();
+                } else {
+                    mOnBatteryStopwatches.get(i).stop();
+                }
+            }
+        }
     }
 
     public Readonly getReadonlyClient() {
@@ -62,5 +87,74 @@
         public boolean isScreenInteractive() {
             return mScreenInteractive;
         }
+
+        /** Creates a {@link TimeInStateStopwatch stopwatch} that tracks the time on battery. */
+        public TimeInStateStopwatch createTimeOnBatteryStopwatch() {
+            synchronized (mStopwatchesLock) {
+                final TimeInStateStopwatch stopwatch = new TimeInStateStopwatch();
+                mOnBatteryStopwatches.add(stopwatch);
+                if (!mCharging) {
+                    stopwatch.start();
+                }
+                return stopwatch;
+            }
+        }
+    }
+
+    /** Tracks the time the device spent in a given state. */
+    public class TimeInStateStopwatch implements AutoCloseable {
+        private final Object mLock = new Object();
+        @GuardedBy("mLock")
+        private long mStartTimeMillis;
+        @GuardedBy("mLock")
+        private long mTotalTimeMillis;
+
+        /** Returns the time in state since the last call to {@link TimeInStateStopwatch#reset}. */
+        public long getMillis() {
+            synchronized (mLock) {
+                return mTotalTimeMillis + elapsedTime();
+            }
+        }
+
+        /** Resets the time in state to 0 without stopping the timer if it's started. */
+        public void reset() {
+            synchronized (mLock) {
+                mTotalTimeMillis = 0;
+                mStartTimeMillis = isRunning() ? SystemClock.elapsedRealtime() : 0;
+            }
+        }
+
+        private void start() {
+            synchronized (mLock) {
+                if (!isRunning()) {
+                    mStartTimeMillis = SystemClock.elapsedRealtime();
+                }
+            }
+        }
+
+        private void stop() {
+            synchronized (mLock) {
+                if (isRunning()) {
+                    mTotalTimeMillis += elapsedTime();
+                    mStartTimeMillis = 0;
+                }
+            }
+        }
+
+        private long elapsedTime() {
+            return isRunning() ? SystemClock.elapsedRealtime() - mStartTimeMillis : 0;
+        }
+
+        @VisibleForTesting
+        public boolean isRunning() {
+            return mStartTimeMillis > 0;
+        }
+
+        @Override
+        public void close() {
+            synchronized (mStopwatchesLock) {
+                mOnBatteryStopwatches.remove(this);
+            }
+        }
     }
 }
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 826cd89..2742b7c 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -103,17 +103,9 @@
 
     /**
      * Default predicate for what UIDs to check for when getting processes. This filters to only
-     * select system UIDs (1000-1999)
+     * select UID 1000 (the {@code system} user)
      */
-    private static final Predicate<Integer> DEFAULT_UID_PREDICATE =
-            uid -> 1000 <= uid && uid < 2000;
-
-    /**
-     * Do not report any threads that have a total CPU usage (across all frequencies) less than or
-     * equal to this number. This significantly reduces the amount of reported threads without
-     * losing any important information
-     */
-    private static final int TOTAL_CPU_USAGE_THRESHOLD_MILLIS = 20;
+    private static final Predicate<Integer> DEFAULT_UID_PREDICATE = uid -> uid == 1000;
 
     /**
      * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
@@ -346,15 +338,6 @@
         }
         int[] cpuUsages = mFrequencyBucketCreator.getBucketedValues(cpuUsagesLong);
 
-        // Filter threads that have low total CPU usage
-        int cpuUsageSum = 0;
-        for (int i = 0; i < cpuUsages.length; i++) {
-            cpuUsageSum += cpuUsages[i];
-        }
-        if (cpuUsageSum <= TOTAL_CPU_USAGE_THRESHOLD_MILLIS) {
-            return null;
-        }
-
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
index cf2a297..de85c1f 100644
--- a/core/java/com/android/internal/os/LooperStats.java
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -49,6 +49,7 @@
     private final int mEntriesSizeCap;
     private int mSamplingInterval;
     private CachedDeviceState.Readonly mDeviceState;
+    private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
     private long mStartTime = System.currentTimeMillis();
     private boolean mAddDebugEntries = false;
 
@@ -58,7 +59,12 @@
     }
 
     public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.close();
+        }
+
         mDeviceState = deviceState;
+        mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
     }
 
     public void setAddDebugEntries(boolean addDebugEntries) {
@@ -148,9 +154,11 @@
         maybeAddSpecialEntry(exportedEntries, mOverflowEntry);
         maybeAddSpecialEntry(exportedEntries, mHashCollisionEntry);
         // Debug entries added to help validate the data.
-        if (mAddDebugEntries) {
+        if (mAddDebugEntries && mBatteryStopwatch != null) {
             exportedEntries.add(createDebugEntry("start_time_millis", mStartTime));
             exportedEntries.add(createDebugEntry("end_time_millis", System.currentTimeMillis()));
+            exportedEntries.add(
+                    createDebugEntry("battery_time_millis", mBatteryStopwatch.getMillis()));
         }
         return exportedEntries;
     }
@@ -168,6 +176,10 @@
         return mStartTime;
     }
 
+    public long getBatteryTimeMillis() {
+        return mBatteryStopwatch != null ? mBatteryStopwatch.getMillis() : 0;
+    }
+
     private void maybeAddSpecialEntry(List<ExportedEntry> exportedEntries, Entry specialEntry) {
         synchronized (specialEntry) {
             if (specialEntry.messageCount > 0 || specialEntry.exceptionCount > 0) {
@@ -188,6 +200,9 @@
             mOverflowEntry.reset();
         }
         mStartTime = System.currentTimeMillis();
+        if (mBatteryStopwatch != null) {
+            mBatteryStopwatch.reset();
+        }
     }
 
     public void setSamplingInterval(int samplingInterval) {
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index b9d53c1..d78bfac 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -120,6 +120,8 @@
         arg5 = null;
         arg6 = null;
         arg7 = null;
+        arg8 = null;
+        arg9 = null;
         argi1 = 0;
         argi2 = 0;
         argi3 = 0;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 3b7ce0a..d8ee643 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -41,6 +41,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Color;
@@ -90,6 +91,7 @@
 import android.view.ViewRootImpl;
 import android.view.ViewRootImpl.ActivityConfigCallback;
 import android.view.Window;
+import android.view.WindowInsetsController;
 import android.view.WindowManager;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -1891,7 +1893,7 @@
                 return true;
             }
             // These are all the recognized media key codes in
-            // KeyEvent.isMediaKey()
+            // KeyEvent.isMediaSessionKey()
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -1992,7 +1994,7 @@
                 return true;
             }
             // These are all the recognized media key codes in
-            // KeyEvent.isMediaKey()
+            // KeyEvent.isMediaSessionKey()
             case KeyEvent.KEYCODE_MEDIA_PLAY:
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
@@ -3876,4 +3878,9 @@
             mDecor.updateLogTag(params);
         }
     }
+
+    @Override
+    public WindowInsetsController getInsetsController() {
+        return mDecor.getWindowInsetsController();
+    }
 }
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
new file mode 100644
index 0000000..100c6ee
--- /dev/null
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.policy;
+
+import com.android.internal.R;
+
+import android.content.res.Resources;
+
+/**
+ * Utility functions for screen decorations used by both window manager and System UI.
+ */
+public class ScreenDecorationsUtils {
+
+    /**
+     * Corner radius that should be used on windows in order to cover the display.
+     * These values are expressed in pixels because they should not respect display or font
+     * scaling, this means that we don't have to reload them on config changes.
+     */
+    public static float getWindowCornerRadius(Resources resources) {
+        // Radius that should be used in case top or bottom aren't defined.
+        float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
+
+        float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
+        if (topRadius == 0) {
+            topRadius = defaultRadius;
+        }
+        float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
+        if (bottomRadius == 0) {
+            bottomRadius = defaultRadius;
+        }
+
+        // Always use the smallest radius to make sure the rounded corners will
+        // completely cover the display.
+        return Math.min(topRadius, bottomRadius);
+    }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 604537f..53b56f2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 
@@ -29,7 +29,7 @@
 {
     void setIcon(String slot, in StatusBarIcon icon);
     void removeIcon(String slot);
-    void disable(int state1, int state2);
+    void disable(int displayId, int state1, int state2);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel(String subPanel);
     void animateCollapsePanels();
@@ -38,8 +38,9 @@
     void showWirelessChargingAnimation(int batteryLevel);
 
     /**
-     * Notifies the status bar of a System UI visibility flag change.
+     * Notifies System UI side of a visibility flag change on the specified display.
      *
+     * @param displayId the id of the display to notify
      * @param vis the visibility flags except SYSTEM_UI_FLAG_LIGHT_STATUS_BAR which will be reported
      *            separately in fullscreenStackVis and dockedStackVis
      * @param fullscreenStackVis the flags which only apply in the region of the fullscreen stack,
@@ -50,13 +51,13 @@
      * @param fullscreenBounds the current bounds of the fullscreen stack, in screen coordinates
      * @param dockedBounds the current bounds of the docked stack, in screen coordinates
      */
-    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
-            in Rect fullscreenBounds, in Rect dockedBounds);
+    void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, in Rect fullscreenBounds, in Rect dockedBounds);
 
-    void topAppWindowChanged(boolean menuVisible);
-    void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+    void topAppWindowChanged(int displayId, boolean menuVisible);
+    void setImeWindowStatus(int displayId, in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
-    void setWindowState(int window, int state);
+    void setWindowState(int display, int window, int state);
 
     void showRecentApps(boolean triggeredFromAltTab);
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
@@ -70,30 +71,38 @@
     void toggleKeyboardShortcutsMenu(int deviceId);
 
     /**
-     * Notifies the status bar that an app transition is pending to delay applying some flags with
-     * visual impact until {@link #appTransitionReady} is called.
-     */
-    void appTransitionPending();
-
-    /**
-     * Notifies the status bar that a pending app transition has been cancelled.
-     */
-    void appTransitionCancelled();
-
-    /**
-     * Notifies the status bar that an app transition is now being executed.
+     * Notifies System UI on the specified display that an app transition is pending to delay
+     * applying some flags with visual impact until {@link #appTransitionReady} is called.
      *
+     * @param displayId the id of the display to notify
+     */
+    void appTransitionPending(int displayId);
+
+    /**
+     * Notifies System UI on the specified display that a pending app transition has been cancelled.
+     *
+     * @param displayId the id of the display to notify
+     */
+    void appTransitionCancelled(int displayId);
+
+    /**
+     * Notifies System UI on the specified display that an app transition is now being executed.
+     *
+     * @param displayId the id of the display to notify
      * @param statusBarAnimationsStartTime the desired start time for all visual animations in the
      *        status bar caused by this app transition in uptime millis
      * @param statusBarAnimationsDuration the duration for all visual animations in the status
      *        bar caused by this app transition in millis
      */
-    void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration);
+    void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
+            long statusBarAnimationsDuration);
 
     /**
-     * Notifies the status bar that an app transition is done.
+     * Notifies System UI on the specified display that an app transition is done.
+     *
+     * @param displayId the id of the display to notify
      */
-    void appTransitionFinished();
+    void appTransitionFinished(int displayId);
 
     void showAssistDisclosure();
     void startAssist(in Bundle args);
@@ -141,7 +150,7 @@
     void showShutdownUi(boolean isReboot, String reason);
 
     // Used to show the dialog when BiometricService starts authentication
-    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+    void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
             boolean requireConfirmation, int userId);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated();
@@ -151,4 +160,6 @@
     void onBiometricError(String error);
     // Used to hide the biometric dialog when the AuthenticationClient is stopped
     void hideBiometricDialog();
+    // Used to request the "try again" button for authentications which requireConfirmation=true
+    void showBiometricTryAgain();
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 69ba070..5118e5f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -16,11 +16,12 @@
 
 package com.android.internal.statusbar;
 
+import android.app.Notification;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -40,6 +41,7 @@
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
+    // TODO(b/117478341): support back button change when IME is showing on a external display.
     void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher);
     void expandSettingsPanel(String subPanel);
@@ -55,7 +57,7 @@
     // Mark current notifications as "seen" and stop ringing, vibrating, blinking.
     void clearNotificationEffects();
     void onNotificationClick(String key, in NotificationVisibility nv);
-    void onNotificationActionClick(String key, int actionIndex, in NotificationVisibility nv);
+    void onNotificationActionClick(String key, int actionIndex, in Notification.Action action, in NotificationVisibility nv, boolean generatedByAssistant);
     void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
     void onClearAllNotifications(int userId);
@@ -68,7 +70,7 @@
     void onNotificationSmartRepliesAdded(in String key, in int replyCount);
     void onNotificationSmartReplySent(in String key, in int replyIndex, in CharSequence reply, boolean generatedByAssistant);
     void onNotificationSettingsViewed(String key);
-    void setSystemUiVisibility(int vis, int mask, String cause);
+    void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
 
     void onGlobalActionsShown();
     void onGlobalActionsHidden();
@@ -91,7 +93,7 @@
     void showPinningEscapeToast();
 
     // Used to show the dialog when BiometricService starts authentication
-    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver, int type,
+    void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
             boolean requireConfirmation, int userId);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated();
@@ -101,4 +103,6 @@
     void onBiometricError(String error);
     // Used to hide the biometric dialog when the AuthenticationClient is stopped
     void hideBiometricDialog();
+    // Used to request the "try again" button for authentications which requireConfirmation=true
+    void showBiometricTryAgain();
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 4b66267..f669e94 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -32,6 +32,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.IntFunction;
 
 /**
  * ArrayUtils contains some methods that you can call to find out
@@ -656,4 +657,30 @@
             throw new ArrayIndexOutOfBoundsException("length=" + len + "; index=" + index);
         }
     }
+
+    /**
+     * Returns an array with values from {@code val} minus {@code null} values
+     *
+     * @param arrayConstructor typically {@code T[]::new} e.g. {@code String[]::new}
+     */
+    public static <T> T[] filterNotNull(T[] val, IntFunction<T[]> arrayConstructor) {
+        int nullCount = 0;
+        int size = size(val);
+        for (int i = 0; i < size; i++) {
+            if (val[i] == null) {
+                nullCount++;
+            }
+        }
+        if (nullCount == 0) {
+            return val;
+        }
+        T[] result = arrayConstructor.apply(size - nullCount);
+        int outIdx = 0;
+        for (int i = 0; i < size; i++) {
+            if (val[i] != null) {
+                result[outIdx++] = val[i];
+            }
+        }
+        return result;
+    }
 }
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 17d5a2e3..6158145 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -28,7 +28,7 @@
 
 /**
  * A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic
- * sugar methods for ByteBuffer. Useful for networking and packet manipulations.
+ * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations.
  * {@hide}
  */
 public final class BitUtils {
@@ -151,4 +151,11 @@
         TextUtils.wrap(builder, "[", "]");
         return builder.toString();
     }
+
+    /**
+     * Converts long to byte array
+     */
+    public static byte[] toBytes(long l) {
+        return ByteBuffer.allocate(8).putLong(l).array();
+    }
 }
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 083c0c9..151901b 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -27,6 +27,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -190,6 +191,13 @@
     }
 
     /**
+     * Returns the size of the given map, or 0 if null
+     */
+    public static int size(@Nullable Map<?, ?> cur) {
+        return cur != null ? cur.size() : 0;
+    }
+
+    /**
      * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null}
      */
     public static boolean isEmpty(@Nullable Collection<?> cur) {
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 5da587b..344d7ef 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -63,7 +63,7 @@
     public static final int
     convertValueToList(CharSequence value, String[] options, int defaultValue)
     {
-        if (null != value) {
+        if (!TextUtils.isEmpty(value)) {
             for (int i = 0; i < options.length; i++) {
                 if (value.equals(options[i]))
                     return i;
@@ -79,8 +79,9 @@
     {
         boolean result = false;
 
-        if (null == value)
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
+        }
 
         if (value.equals("1")
         ||  value.equals("true")
@@ -94,8 +95,9 @@
     public static final int
     convertValueToInt(CharSequence charSeq, int defaultValue)
     {
-        if (null == charSeq)
+        if (TextUtils.isEmpty(charSeq)) {
             return defaultValue;
+        }
 
         String nm = charSeq.toString();
 
@@ -138,7 +140,7 @@
     }
 
     public static int convertValueToUnsignedInt(String value, int defaultValue) {
-        if (null == value) {
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
         }
 
@@ -1674,7 +1676,7 @@
     public static boolean readBooleanAttribute(XmlPullParser in, String name,
             boolean defaultValue) {
         final String value = in.getAttributeValue(null, name);
-        if (value == null) {
+        if (TextUtils.isEmpty(value)) {
             return defaultValue;
         } else {
             return Boolean.parseBoolean(value);
@@ -1711,7 +1713,7 @@
 
     public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
         final String value = in.getAttributeValue(null, name);
-        if (value != null) {
+        if (!TextUtils.isEmpty(value)) {
             return Base64.decode(value, Base64.DEFAULT);
         } else {
             return null;
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaConsumer.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaConsumer.java
index 27d25b8..3e7ce2b 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaConsumer.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Consumer}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface NonaConsumer<A, B, C, D, E, F, G, H, I> {
+    void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaFunction.java
index 27d25b8..560b4f1 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaFunction.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Function}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface NonaFunction<A, B, C, D, E, F, G, H, I, R> {
+    R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/NonaPredicate.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/NonaPredicate.java
index 27d25b8..c1e6f37 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/NonaPredicate.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 9-argument {@link Predicate}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface NonaPredicate<A, B, C, D, E, F, G, H, I> {
+    boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctConsumer.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctConsumer.java
index 27d25b8..83ee305 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctConsumer.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Consumer}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface OctConsumer<A, B, C, D, E, F, G, H> {
+    void accept(A a, B b, C c, D d, E e, F f, G g, H h);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctFunction.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctFunction.java
index 27d25b8..cb16624 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctFunction.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Function}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface OctFunction<A, B, C, D, E, F, G, H, R> {
+    R apply(A a, B b, C c, D d, E e, F f, G g, H h);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/com/android/internal/util/function/OctPredicate.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/java/com/android/internal/util/function/OctPredicate.java
index 27d25b8..7f36d6a 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/com/android/internal/util/function/OctPredicate.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
+ * A 8-argument {@link Predicate}
+ *
  * @hide
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public interface OctPredicate<A, B, C, D, E, F, G, H> {
+    boolean test(A a, B b, C c, D d, E e, F f, G g, H h);
 }
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index 4ffe441..d74e715 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -22,6 +22,10 @@
 import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuintConsumer;
@@ -39,61 +43,62 @@
  *
  * @hide
  */
-abstract class OmniFunction<A, B, C, D, E, F, G, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, G, H, I, R> implements
         PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
         QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
         HexFunction<A, B, C, D, E, F, R>, HeptFunction<A, B, C, D, E, F, G, R>,
+        OctFunction<A, B, C, D, E, F, G, H, R>, NonaFunction<A, B, C, D, E, F, G, H, I, R>,
         PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
         QuintConsumer<A, B, C, D, E>, HexConsumer<A, B, C, D, E, F>,
-        HeptConsumer<A, B, C, D, E, F, G>,
-        PooledPredicate<A>, BiPredicate<A, B>,
+        HeptConsumer<A, B, C, D, E, F, G>, OctConsumer<A, B, C, D, E, F, G, H>,
+        NonaConsumer<A, B, C, D, E, F, G, H, I>, PooledPredicate<A>, BiPredicate<A, B>,
         PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
         PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
 
-    abstract R invoke(A a, B b, C c, D d, E e, F f, G g);
+    abstract R invoke(A a, B b, C c, D d, E e, F f, G g, H h, I i);
 
     @Override
     public R apply(A o, B o2) {
-        return invoke(o, o2, null, null, null, null, null);
+        return invoke(o, o2, null, null, null, null, null, null, null);
     }
 
     @Override
     public R apply(A o) {
-        return invoke(o, null, null, null, null, null, null);
+        return invoke(o, null, null, null, null, null, null, null, null);
     }
 
-    public abstract <V> OmniFunction<A, B, C, D, E, F, G, V> andThen(
+    public abstract <V> OmniFunction<A, B, C, D, E, F, G, H, I, V> andThen(
             Function<? super R, ? extends V> after);
-    public abstract OmniFunction<A, B, C, D, E, F, G, R> negate();
+    public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> negate();
 
     @Override
     public void accept(A o, B o2) {
-        invoke(o, o2, null, null, null, null, null);
+        invoke(o, o2, null, null, null, null, null, null, null);
     }
 
     @Override
     public void accept(A o) {
-        invoke(o, null, null, null, null, null, null);
+        invoke(o, null, null, null, null, null, null, null, null);
     }
 
     @Override
     public void run() {
-        invoke(null, null, null, null, null, null, null);
+        invoke(null, null, null, null, null, null, null, null, null);
     }
 
     @Override
     public R get() {
-        return invoke(null, null, null, null, null, null, null);
+        return invoke(null, null, null, null, null, null, null, null, null);
     }
 
     @Override
     public boolean test(A o, B o2) {
-        return (Boolean) invoke(o, o2, null, null, null, null, null);
+        return (Boolean) invoke(o, o2, null, null, null, null, null, null, null);
     }
 
     @Override
     public boolean test(A o) {
-        return (Boolean) invoke(o, null, null, null, null, null, null);
+        return (Boolean) invoke(o, null, null, null, null, null, null, null, null);
     }
 
     @Override
@@ -108,52 +113,72 @@
 
     @Override
     public R apply(A a, B b, C c) {
-        return invoke(a, b, c, null, null, null, null);
+        return invoke(a, b, c, null, null, null, null, null, null);
     }
 
     @Override
     public void accept(A a, B b, C c) {
-        invoke(a, b, c, null, null, null, null);
+        invoke(a, b, c, null, null, null, null, null, null);
     }
 
     @Override
     public R apply(A a, B b, C c, D d) {
-        return invoke(a, b, c, d, null, null, null);
+        return invoke(a, b, c, d, null, null, null, null, null);
     }
 
     @Override
     public R apply(A a, B b, C c, D d, E e) {
-        return invoke(a, b, c, d, e, null, null);
+        return invoke(a, b, c, d, e, null, null, null, null);
     }
 
     @Override
     public R apply(A a, B b, C c, D d, E e, F f) {
-        return invoke(a, b, c, d, e, f, null);
+        return invoke(a, b, c, d, e, f, null, null, null);
     }
 
     @Override
     public R apply(A a, B b, C c, D d, E e, F f, G g) {
-        return invoke(a, b, c, d, e, f, g);
+        return invoke(a, b, c, d, e, f, g, null, null);
+    }
+
+    @Override
+    public R apply(A a, B b, C c, D d, E e, F f, G g, H h) {
+        return invoke(a, b, c, d, e, f, g, h, null);
+    }
+
+    @Override
+    public R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+        return invoke(a, b, c, d, e, f, g, h, i);
     }
 
     @Override
     public void accept(A a, B b, C c, D d) {
-        invoke(a, b, c, d, null, null, null);
+        invoke(a, b, c, d, null, null, null, null, null);
     }
 
     @Override
     public void accept(A a, B b, C c, D d, E e) {
-        invoke(a, b, c, d, e, null, null);
+        invoke(a, b, c, d, e, null, null, null, null);
     }
 
     @Override
     public void accept(A a, B b, C c, D d, E e, F f) {
-        invoke(a, b, c, d, e, f, null);
+        invoke(a, b, c, d, e, f, null, null, null);
     }
 
     @Override
     public void accept(A a, B b, C c, D d, E e, F f, G g) {
-        invoke(a, b, c, d, e, f, g);
+        invoke(a, b, c, d, e, f, g, null, null);
+    }
+
+    @Override
+    public void accept(A a, B b, C c, D d, E e, F f, G g, H h) {
+        invoke(a, b, c, d, e, f, g, h, null);
+    }
+
+    @Override
+    public void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+        invoke(a, b, c, d, e, f, g, h, i);
     }
 
     @Override
@@ -167,5 +192,5 @@
     }
 
     @Override
-    public abstract OmniFunction<A, B, C, D, E, F, G, R> recycleOnUse();
+    public abstract OmniFunction<A, B, C, D, E, F, G, H, I, R> recycleOnUse();
 }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index af3c752..c00932e 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -25,6 +25,10 @@
 import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuintConsumer;
@@ -176,7 +180,8 @@
             Consumer<? super A> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+                function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -192,7 +197,8 @@
             Predicate<? super A> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null);
+                function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -208,7 +214,8 @@
             Function<? super A, ? extends R> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null);
+                function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -238,7 +245,8 @@
             A arg1) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null);
+                    function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -257,7 +265,8 @@
             BiConsumer<? super A, ? super B> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+                function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -274,7 +283,8 @@
             BiPredicate<? super A, ? super B> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+                function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -291,7 +301,8 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+                function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -308,7 +319,8 @@
             BiConsumer<? super A, ? super B> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -325,7 +337,8 @@
             BiPredicate<? super A, ? super B> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -342,7 +355,8 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -359,7 +373,8 @@
             BiConsumer<? super A, ? super B> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -376,7 +391,8 @@
             BiPredicate<? super A, ? super B> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -393,7 +409,8 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null);
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -424,7 +441,8 @@
             A arg1, B arg2) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null);
+                    function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -444,7 +462,8 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -462,7 +481,8 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -480,7 +500,8 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -498,7 +519,8 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -516,7 +538,8 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -534,7 +557,8 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -552,7 +576,8 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -570,7 +595,8 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
+                null);
     }
 
     /**
@@ -602,7 +628,8 @@
             A arg1, B arg2, C arg3) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null);
+                    function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -623,7 +650,8 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -642,7 +670,8 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -661,7 +690,8 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -680,7 +710,8 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -699,7 +730,8 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -718,7 +750,8 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -737,7 +770,8 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -756,7 +790,8 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -775,7 +810,8 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -794,7 +830,8 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
+                null);
     }
 
     /**
@@ -827,7 +864,8 @@
             A arg1, B arg2, C arg3, D arg4) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null);
+                    function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -849,7 +887,8 @@
             QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
             A arg1, B arg2, C arg3, D arg4, E arg5) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+                function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+                null);
     }
 
     /**
@@ -869,7 +908,8 @@
             QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
                     function, A arg1, B arg2, C arg3, D arg4, E arg5) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null);
+                function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
+                null);
     }
 
     /**
@@ -904,7 +944,8 @@
             A arg1, B arg2, C arg3, D arg4, E arg5) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null);
+                    function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -927,7 +968,8 @@
             HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+                function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+                null);
     }
 
     /**
@@ -948,7 +990,8 @@
             HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
                     ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null);
+                function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+                null);
     }
 
     /**
@@ -984,7 +1027,8 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null);
+                    function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
+                    null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1008,7 +1052,8 @@
             HeptConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
                     ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+                function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+                null);
     }
 
     /**
@@ -1031,7 +1076,8 @@
                     ? super G, ? extends R> function,
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+                function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+                null);
     }
 
     /**
@@ -1068,7 +1114,195 @@
                     ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+                    function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
+                    null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+     */
+    static <A, B, C, D, E, F, G, H> PooledRunnable obtainRunnable(
+            OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+                    ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+            H arg8) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
+     */
+    static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier(
+            OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                                ? super G, ? super H, ? extends R> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                null);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+     * arg7, arg8) } when handled
+     */
+    static <A, B, C, D, E, F, G, H> Message obtainMessage(
+            OctConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F, ? super G,
+                    ? super H> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7,
+            H arg8) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                    null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+     */
+    static <A, B, C, D, E, F, G, H, I> PooledRunnable obtainRunnable(
+            NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                    ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+            E arg5, F arg6, G arg7, H arg8, I arg9) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                arg9);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
+     */
+    static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier(
+            NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                                ? super G, ? super H, ? super I, ? extends R> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                arg9);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+     * arg7, arg8, arg9) } when handled
+     */
+    static <A, B, C, D, E, F, G, H, I> Message obtainMessage(
+            NonaConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                    ? super G, ? super H, ? super I> function, A arg1, B arg2, C arg3, D arg4,
+            E arg5, F arg6, G arg7, H arg8, I arg9) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                    arg9);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index eea1e5f..6be626a 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -30,6 +30,12 @@
 import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.HexPredicate;
+import com.android.internal.util.function.NonaConsumer;
+import com.android.internal.util.function.NonaFunction;
+import com.android.internal.util.function.NonaPredicate;
+import com.android.internal.util.function.OctConsumer;
+import com.android.internal.util.function.OctFunction;
+import com.android.internal.util.function.OctPredicate;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuadPredicate;
@@ -54,12 +60,12 @@
  * @hide
  */
 final class PooledLambdaImpl<R> extends OmniFunction<Object,
-        Object, Object, Object, Object, Object, Object, R> {
+        Object, Object, Object, Object, Object, Object, Object, Object, R> {
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "PooledLambdaImpl";
 
-    private static final int MAX_ARGS = 7;
+    private static final int MAX_ARGS = 9;
 
     private static final int MAX_POOL_SIZE = 50;
 
@@ -125,7 +131,7 @@
 
     /**
      * Bit schema:
-     * AAAAAAABCDEEEEEEFFFFFF
+     * AAAAAAAAABCDEEEEEEFFFFFF
      *
      * Where:
      * A - whether {@link #mArgs arg} at corresponding index was specified at
@@ -161,17 +167,19 @@
     }
 
     @Override
-    R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
+    R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7,
+            Object a8, Object a9) {
         checkNotRecycled();
         if (DEBUG) {
             Log.i(LOG_TAG, this + ".invoke("
                     + commaSeparateFirstN(
-                            new Object[] { a1, a2, a3, a4, a5, a6, a7 },
+                            new Object[] { a1, a2, a3, a4, a5, a6, a7, a8, a9 },
                             LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
                     + ")");
         }
-        final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3)
-                && fillInArg(a4) && fillInArg(a5) && fillInArg(a6) && fillInArg(a7);
+        final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4)
+                && fillInArg(a5) && fillInArg(a6) && fillInArg(a7) && fillInArg(a8)
+                && fillInArg(a9);
         int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
         if (argCount != LambdaType.MASK_ARG_COUNT) {
             for (int i = 0; i < argCount; i++) {
@@ -335,7 +343,7 @@
                                 popArg(2), popArg(3), popArg(4), popArg(5));
                     }
                 }
-            }
+            } break;
 
             case 7: {
                 switch (returnType) {
@@ -356,7 +364,49 @@
                                 popArg(5), popArg(6));
                     }
                 }
-            }
+            } break;
+
+            case 8: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((OctConsumer) mFunc).accept(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4),
+                                popArg(5), popArg(6), popArg(7));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((OctPredicate) mFunc).test(popArg(0),
+                                popArg(1), popArg(2), popArg(3),
+                                popArg(4), popArg(5), popArg(6), popArg(7));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((OctFunction) mFunc).apply(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4),
+                                popArg(5), popArg(6), popArg(7));
+                    }
+                }
+            } break;
+
+            case 9: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((NonaConsumer) mFunc).accept(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5),
+                                popArg(6), popArg(7), popArg(8));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((NonaPredicate) mFunc).test(popArg(0),
+                                popArg(1), popArg(2), popArg(3), popArg(4),
+                                popArg(5), popArg(6), popArg(7), popArg(8));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((NonaFunction) mFunc).apply(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5),
+                                popArg(6), popArg(7), popArg(8));
+                    }
+                }
+            } break;
         }
         throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
     }
@@ -419,8 +469,8 @@
      * Internal non-typesafe factory method for {@link PooledLambdaImpl}
      */
     static <E extends PooledLambda> E acquire(Pool pool, Object func,
-            int fNumArgs, int numPlaceholders, int fReturnType,
-            Object a, Object b, Object c, Object d, Object e, Object f, Object g) {
+            int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
+            Object d, Object e, Object f, Object g, Object h, Object i) {
         PooledLambdaImpl r = acquire(pool);
         if (DEBUG) {
             Log.i(LOG_TAG,
@@ -436,6 +486,8 @@
                             + ", e = " + e
                             + ", f = " + f
                             + ", g = " + g
+                            + ", h = " + h
+                            + ", i = " + i
                             + ")");
         }
         r.mFunc = func;
@@ -449,6 +501,8 @@
         setIfInBounds(r.mArgs, 4, e);
         setIfInBounds(r.mArgs, 5, f);
         setIfInBounds(r.mArgs, 6, g);
+        setIfInBounds(r.mArgs, 7, h);
+        setIfInBounds(r.mArgs, 8, i);
         return (E) r;
     }
 
@@ -474,13 +528,14 @@
     }
 
     @Override
-    public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> negate() {
+    public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+            R> negate() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, V> andThen(
-            Function<? super R, ? extends V> after) {
+    public <V> OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+            V> andThen(Function<? super R, ? extends V> after) {
         throw new UnsupportedOperationException();
     }
 
@@ -500,7 +555,8 @@
     }
 
     @Override
-    public OmniFunction<Object, Object, Object, Object, Object, Object, Object, R> recycleOnUse() {
+    public OmniFunction<Object, Object, Object, Object, Object, Object, Object, Object, Object,
+            R> recycleOnUse() {
         if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
         mFlags |= FLAG_RECYCLE_ON_USE;
         return this;
@@ -584,6 +640,8 @@
                 case 5: return "Quint";
                 case 6: return "Hex";
                 case 7: return "Hept";
+                case 8: return "Oct";
+                case 9: return "Nona";
                 default: throw new IllegalArgumentException("" + argCount);
             }
         }
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 137ca7f..c8834a8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -27,6 +27,8 @@
 import android.view.IWindow;
 import android.view.IWindowSession;
 import android.view.PointerIcon;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -53,6 +55,15 @@
     }
 
     @Override
+    public void insetsChanged(InsetsState insetsState) {
+    }
+
+    @Override
+    public void insetsControlChanged(InsetsState insetsState,
+            InsetsSourceControl[] activeControls) throws RemoteException {
+    }
+
+    @Override
     public void moved(int newX, int newY) {
     }
 
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 7635a72..b7e656b 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -814,7 +814,14 @@
             final View child = getChildAt(i);
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.alwaysShow && child.getVisibility() != GONE) {
-                measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                if (lp.maxHeight != -1) {
+                    final int remainingHeight = heightSize - heightUsed;
+                    measureChildWithMargins(child, widthSpec, widthPadding,
+                            MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+                            lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+                } else {
+                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                }
                 heightUsed += child.getMeasuredHeight();
             }
         }
@@ -824,9 +831,17 @@
         // And now the rest.
         for (int i = 0; i < childCount; i++) {
             final View child = getChildAt(i);
+
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (!lp.alwaysShow && child.getVisibility() != GONE) {
-                measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                if (lp.maxHeight != -1) {
+                    final int remainingHeight = heightSize - heightUsed;
+                    measureChildWithMargins(child, widthSpec, widthPadding,
+                            MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST),
+                            lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0);
+                } else {
+                    measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
+                }
                 heightUsed += child.getMeasuredHeight();
             }
         }
@@ -938,6 +953,7 @@
         public boolean alwaysShow;
         public boolean ignoreOffset;
         public boolean hasNestedScrollIndicator;
+        public int maxHeight;
 
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
@@ -953,6 +969,8 @@
             hasNestedScrollIndicator = a.getBoolean(
                     R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator,
                     false);
+            maxHeight = a.getDimensionPixelSize(
+                    R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1);
             a.recycle();
         }
 
@@ -965,6 +983,7 @@
             this.alwaysShow = source.alwaysShow;
             this.ignoreOffset = source.ignoreOffset;
             this.hasNestedScrollIndicator = source.hasNestedScrollIndicator;
+            this.maxHeight = source.maxHeight;
         }
 
         public LayoutParams(MarginLayoutParams source) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 8495850..b97a9fa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -934,7 +934,7 @@
         // If the storage model feature flag is disabled, we need to fiddle
         // around with permission definitions to return us to pre-Q behavior.
         // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (!StorageManager.hasIsolatedStorage()) {
             if (newPermissions.contains(android.Manifest.permission.READ_MEDIA_AUDIO) ||
                     newPermissions.contains(android.Manifest.permission.READ_MEDIA_VIDEO) ||
                     newPermissions.contains(android.Manifest.permission.READ_MEDIA_IMAGES)) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bdd5f83..31bb1d5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -34,7 +34,6 @@
     ],
 
     cppflags: ["-Wno-conversion-null"],
-    cpp_std: "c++17",
 
     srcs: [
         "AndroidRuntime.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index eada690..f9879cc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -1056,12 +1056,18 @@
     if (rootDir == NULL) {
         rootDir = "/system";
         if (!hasDir("/system")) {
-            LOG_FATAL("No root directory specified, and /android does not exist.");
+            LOG_FATAL("No root directory specified, and /system does not exist.");
             return;
         }
         setenv("ANDROID_ROOT", rootDir, 1);
     }
 
+    const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
+    if (runtimeRootDir == NULL) {
+        LOG_FATAL("No runtime directory specified with ANDROID_RUNTIME_ROOT environment variable.");
+        return;
+    }
+
     //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
     //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
 
@@ -1450,8 +1456,8 @@
     REG_JNI(register_android_hardware_UsbDeviceConnection),
     REG_JNI(register_android_hardware_UsbRequest),
     REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
-    REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioSystem),
+    REG_JNI(register_android_media_AudioRecord),
     REG_JNI(register_android_media_AudioTrack),
     REG_JNI(register_android_media_JetPlayer),
     REG_JNI(register_android_media_MicrophoneInfo),
diff --git a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
index dcb7874..cfe742d 100644
--- a/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
+++ b/core/jni/android/graphics/HarfBuzzNGFaceSkia.cpp
@@ -59,7 +59,7 @@
 static void SkiaGetGlyphWidthAndExtents(SkPaint* paint, hb_codepoint_t codepoint, hb_position_t* width, hb_glyph_extents_t* extents)
 {
     ALOG_ASSERT(codepoint <= 0xFFFF);
-    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    paint->setTextEncoding(kGlyphID_SkTextEncoding);
 
     SkScalar skWidth;
     SkRect skBounds;
@@ -84,7 +84,7 @@
 {
     HarfBuzzFontData* hbFontData = reinterpret_cast<HarfBuzzFontData*>(fontData);
     SkPaint* paint = hbFontData->m_paint;
-    paint->setTextEncoding(SkPaint::kUTF32_TextEncoding);
+    paint->setTextEncoding(kUTF32_SkTextEncoding);
 
     if (unicode > 0x10ffff) {
         unicode = 0xfffd;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index a8b0640..c249e20 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -71,7 +71,7 @@
 
 static void defaultSettingsForAndroid(Paint* paint) {
     // GlyphID encoding is required because we are using Harfbuzz shaping
-    paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+    paint->setTextEncoding(kGlyphID_SkTextEncoding);
 }
 
 namespace PaintGlue {
@@ -321,7 +321,7 @@
         x += MinikinUtils::xOffsetForTextAlign(paint, layout);
         Paint::Align align = paint->getTextAlign();
         paint->setTextAlign(Paint::kLeft_Align);
-        paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
+        paint->setTextEncoding(kGlyphID_SkTextEncoding);
         GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
         MinikinUtils::forFontRun(layout, paint, f);
         paint->setTextAlign(align);
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 68f5bef..ed6a84b 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -64,11 +64,10 @@
         jint tileModeX, jint tileModeY) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
-    sk_sp<SkColorFilter> colorFilter;
     if (jbitmap) {
         // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
         // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
-        image = android::bitmap::toBitmap(env, jbitmap).makeImage(&colorFilter);
+        image = android::bitmap::toBitmap(env, jbitmap).makeImage();
     }
 
     if (!image.get()) {
@@ -81,9 +80,6 @@
     if (matrix) {
         shader = shader->makeWithLocalMatrix(*matrix);
     }
-    if(colorFilter) {
-        shader = shader->makeWithColorFilter(colorFilter);
-    }
 
     ThrowIAE_IfNull(env, shader.get());
     return reinterpret_cast<jlong>(shader.release());
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index e02741f..719cf74 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -514,14 +514,14 @@
 
     sp<ANativeWindow> anw;
     if ((anw = getNativeWindow(env, surface)) == NULL) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Could not retrieve native window from surface.");
         return BAD_VALUE;
     }
     int32_t usage = 0;
     status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
     if(err != NO_ERROR) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Error while querying surface usage bits");
         OVERRIDE_SURFACE_ERROR(err);
         return err;
@@ -542,7 +542,7 @@
 
     status_t err = native_window_api_disconnect(anw.get(), NATIVE_WINDOW_API_CAMERA);
     if(err != NO_ERROR) {
-        jniThrowException(env, "java/lang/UnsupportedOperationException;",
+        jniThrowException(env, "java/lang/UnsupportedOperationException",
             "Error while disconnecting surface");
         OVERRIDE_SURFACE_ERROR(err);
         return err;
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 05f6556..e74aafe 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
     jfieldID deviceWidth;
     jfieldID deviceHeight;
     jfieldID uniqueId;
+    jfieldID physicalPort;
     jfieldID type;
 } gDisplayViewportClassInfo;
 
@@ -54,6 +55,9 @@
 
 status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewportObj,
         DisplayViewport* viewport) {
+    static const jclass byteClass = FindClassOrDie(env, "java/lang/Byte");
+    static const jmethodID byteValue = env->GetMethodID(byteClass, "byteValue", "()B");
+
     viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
     viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
     viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
@@ -65,6 +69,12 @@
         viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
     }
 
+    viewport->physicalPort = std::nullopt;
+    jobject physicalPort = env->GetObjectField(viewportObj, gDisplayViewportClassInfo.physicalPort);
+    if (physicalPort != nullptr) {
+        viewport->physicalPort = std::make_optional(env->CallByteMethod(physicalPort, byteValue));
+    }
+
     viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
                 gDisplayViewportClassInfo.type));
 
@@ -112,6 +122,9 @@
     gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
             gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
 
+    gDisplayViewportClassInfo.physicalPort = GetFieldIDOrDie(env,
+            gDisplayViewportClassInfo.clazz, "physicalPort", "Ljava/lang/Byte;");
+
     gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
             gDisplayViewportClassInfo.clazz, "type", "I");
 
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 5887fa7..10005dd 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -22,6 +22,7 @@
 #include <utils/threads.h>
 
 #include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
 
 namespace android {
 
@@ -29,6 +30,7 @@
     jfieldID ptr;
     jfieldID name;
     jfieldID dispatchingTimeoutNanos;
+    jfieldID token;
 } gInputApplicationHandleClassInfo;
 
 static Mutex gHandleMutex;
@@ -75,6 +77,15 @@
     mInfo->dispatchingTimeout = env->GetLongField(obj,
             gInputApplicationHandleClassInfo.dispatchingTimeoutNanos);
 
+    jobject tokenObj = env->GetObjectField(obj,
+            gInputApplicationHandleClassInfo.token);
+    if (tokenObj) {
+        mInfo->token = ibinderForJavaObject(env, tokenObj);
+        env->DeleteLocalRef(tokenObj);
+    } else {
+        mInfo->token.clear();
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -153,6 +164,9 @@
             clazz,
             "dispatchingTimeoutNanos", "J");
 
+    GET_FIELD_ID(gInputApplicationHandleClassInfo.token, clazz,
+            "token", "Landroid/os/IBinder;");
+
     return 0;
 }
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 6ecb5de..76920f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -21,19 +21,19 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include <android_view_InputChannel.h>
 #include <android/graphics/Region.h>
 #include <ui/Region.h>
 
 #include "android_hardware_input_InputWindowHandle.h"
 #include "android_hardware_input_InputApplicationHandle.h"
+#include "android_util_Binder.h"
 
 namespace android {
 
 static struct {
     jfieldID ptr;
     jfieldID inputApplicationHandle;
-    jfieldID inputChannel;
+    jfieldID token;
     jfieldID name;
     jfieldID layoutParamsFlags;
     jfieldID layoutParamsType;
@@ -42,6 +42,7 @@
     jfieldID frameTop;
     jfieldID frameRight;
     jfieldID frameBottom;
+    jfieldID surfaceInset;
     jfieldID scaleFactor;
     jfieldID touchableRegion;
     jfieldID visible;
@@ -61,9 +62,7 @@
 
 // --- NativeInputWindowHandle ---
 
-NativeInputWindowHandle::NativeInputWindowHandle(
-        const sp<InputApplicationHandle>& inputApplicationHandle, jweak objWeak) :
-        InputWindowHandle(inputApplicationHandle),
+NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) :
         mObjWeak(objWeak) {
 }
 
@@ -86,13 +85,12 @@
 
     mInfo.touchableRegion.clear();
 
-    jobject inputChannelObj = env->GetObjectField(obj,
-            gInputWindowHandleClassInfo.inputChannel);
-    if (inputChannelObj) {
-        mInfo.inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj);
-        env->DeleteLocalRef(inputChannelObj);
+    jobject tokenObj = env->GetObjectField(obj,
+            gInputWindowHandleClassInfo.token);
+    if (tokenObj) {
+        mInfo.token = ibinderForJavaObject(env, tokenObj);
     } else {
-        mInfo.inputChannel.clear();
+        mInfo.token.clear();
     }
 
     jstring nameObj = jstring(env->GetObjectField(obj,
@@ -120,7 +118,9 @@
             gInputWindowHandleClassInfo.frameRight);
     mInfo.frameBottom = env->GetIntField(obj,
             gInputWindowHandleClassInfo.frameBottom);
-    mInfo.scaleFactor = env->GetFloatField(obj,
+    mInfo.surfaceInset = env->GetIntField(obj,
+            gInputWindowHandleClassInfo.surfaceInset);
+    mInfo.globalScaleFactor = env->GetFloatField(obj,
             gInputWindowHandleClassInfo.scaleFactor);
 
     jobject regionObj = env->GetObjectField(obj,
@@ -155,6 +155,18 @@
     mInfo.displayId = env->GetIntField(obj,
             gInputWindowHandleClassInfo.displayId);
 
+    jobject inputApplicationHandleObj = env->GetObjectField(obj,
+            gInputWindowHandleClassInfo.inputApplicationHandle);
+    if (inputApplicationHandleObj) {
+        sp<InputApplicationHandle> inputApplicationHandle =
+            android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+        if (inputApplicationHandle != nullptr) {
+            inputApplicationHandle->updateInfo();
+            mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
+        }
+        env->DeleteLocalRef(inputApplicationHandleObj);
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -175,14 +187,8 @@
     if (ptr) {
         handle = reinterpret_cast<NativeInputWindowHandle*>(ptr);
     } else {
-        jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj,
-                gInputWindowHandleClassInfo.inputApplicationHandle);
-        sp<InputApplicationHandle> inputApplicationHandle =
-                android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
-        env->DeleteLocalRef(inputApplicationHandleObj);
-
         jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj);
-        handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak);
+        handle = new NativeInputWindowHandle(objWeak);
         handle->incStrong((void*)android_server_InputWindowHandle_getHandle);
         env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr,
                 reinterpret_cast<jlong>(handle));
@@ -236,8 +242,8 @@
             clazz,
             "inputApplicationHandle", "Landroid/view/InputApplicationHandle;");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz,
-            "inputChannel", "Landroid/view/InputChannel;");
+    GET_FIELD_ID(gInputWindowHandleClassInfo.token, clazz,
+            "token", "Landroid/os/IBinder;");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz,
             "name", "Ljava/lang/String;");
@@ -263,6 +269,9 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz,
             "frameBottom", "I");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.surfaceInset, clazz,
+            "surfaceInset", "I");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz,
             "scaleFactor", "F");
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 2be267e..54b89f5 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -26,8 +26,7 @@
 
 class NativeInputWindowHandle : public InputWindowHandle {
 public:
-    NativeInputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
-            jweak objWeak);
+    NativeInputWindowHandle(jweak objWeak);
     virtual ~NativeInputWindowHandle();
 
     jobject getInputWindowHandleObjLocalRef(JNIEnv* env);
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 1ea4ed1..12a8343b4 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -842,6 +842,18 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioRecord_get_port_id(JNIEnv *env,  jobject thiz) {
+    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
+    if (lpRecorder == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Unable to retrieve AudioRecord pointer for getId()");
+        return (jint)AUDIO_PORT_HANDLE_NONE;
+    }
+    return (jint)lpRecorder->getPortId();
+}
+
+
+// ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,               signature,  funcPtr
@@ -883,6 +895,7 @@
                                        (void *)android_media_AudioRecord_get_timestamp},
     {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
                                         (void *)android_media_AudioRecord_get_active_microphones},
+    {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
 };
 
 // field names found in android/media/AudioRecord.java
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7410b52..adab8e2 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -175,6 +175,17 @@
     jmethodID postRecordConfigEventFromNative;
 } gAudioPolicyEventHandlerMethods;
 
+//
+// JNI Initialization for OpenSLES routing
+//
+jmethodID gMidAudioTrackRoutingProxy_ctor;
+jmethodID gMidAudioTrackRoutingProxy_release;
+jmethodID gMidAudioRecordRoutingProxy_ctor;
+jmethodID gMidAudioRecordRoutingProxy_release;
+
+jclass gClsAudioTrackRoutingProxy;
+jclass gClsAudioRecordRoutingProxy;
+
 static Mutex gLock;
 
 enum AudioError {
@@ -2017,6 +2028,39 @@
     return (jint)nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) {
+    return FCC_8;
+}
+
+static jint
+android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid)
+{
+    status_t status = AudioSystem::setAssistantUid(uid);
+    return (jint)nativeToJavaStatus(status);
+}
+
+static jint
+android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
+    std::vector<uid_t> nativeUidsVector;
+
+    if (uids != nullptr) {
+       jsize len = env->GetArrayLength(uids);
+
+       if (len > 0) {
+           int *nativeUids = nullptr;
+           nativeUids = env->GetIntArrayElements(uids, 0);
+           if (nativeUids != nullptr) {
+               for (size_t i = 0; i < len; i++) {
+                   nativeUidsVector.push_back(nativeUids[i]);
+               }
+               env->ReleaseIntArrayElements(uids, nativeUids, 0);
+           }
+       }
+    }
+    status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector);
+    return (jint)nativeToJavaStatus(status);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -2077,9 +2121,10 @@
     {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
     {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
     {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+    {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+    {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
 };
 
-
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
         "(Ljava/lang/Object;)V",
@@ -2089,8 +2134,15 @@
         (void *)android_media_AudioSystem_eventHandlerFinalize},
 };
 
+static const JNINativeMethod gGetFCC8Methods[] = {
+    {"native_get_FCC_8", "()I", (void *)android_media_AudioSystem_get_FCC_8},
+};
+
 int register_android_media_AudioSystem(JNIEnv *env)
 {
+    // This needs to be done before hooking up methods AudioTrackRoutingProxy (below)
+    RegisterMethodsOrDie(env, kClassPathName, gGetFCC8Methods, NELEM(gGetFCC8Methods));
+
     jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
     gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
     gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
@@ -2247,6 +2299,28 @@
     gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
             audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
 
+    // AudioTrackRoutingProxy methods
+    gClsAudioTrackRoutingProxy =
+            android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy");
+    // make sure this reference doesn't get deleted
+    gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy);
+
+    gMidAudioTrackRoutingProxy_ctor =
+            android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V");
+    gMidAudioTrackRoutingProxy_release =
+            android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "native_release", "()V");
+
+    // AudioRecordRoutingProxy
+    gClsAudioRecordRoutingProxy =
+            android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy");
+    // make sure this reference doesn't get deleted
+    gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy);
+
+    gMidAudioRecordRoutingProxy_ctor =
+            android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V");
+    gMidAudioRecordRoutingProxy_release =
+            android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "native_release", "()V");
+
     AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
 
     RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index bf22dd2..d927972 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -1226,10 +1226,6 @@
     pJniStorage->mDeviceCallback.clear();
 }
 
-static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
-    return FCC_8;
-}
-
 // Pass through the arguments to the AudioFlinger track implementation.
 static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
         jobject jconfig, jobject joperation) {
@@ -1288,6 +1284,17 @@
 }
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_get_port_id(JNIEnv *env,  jobject thiz) {
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "AudioTrack not initialized");
+        return (jint)AUDIO_PORT_HANDLE_NONE;
+    }
+    return (jint)lpTrack->getPortId();
+}
+
+// ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 static const JNINativeMethod gMethods[] = {
     // name,              signature,     funcPtr
@@ -1351,7 +1358,6 @@
     {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
     {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
     {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
-    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
     {"native_applyVolumeShaper",
             "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
                                          (void *)android_media_AudioTrack_apply_volume_shaper},
@@ -1359,6 +1365,7 @@
             "(I)Landroid/media/VolumeShaper$State;",
                                         (void *)android_media_AudioTrack_get_volume_shaper_state},
     {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
+    {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
 };
 
 
@@ -1384,7 +1391,6 @@
     }
 }
 
-
 // ----------------------------------------------------------------------------
 int register_android_media_AudioTrack(JNIEnv *env)
 {
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 49d5007..fa1da4b 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -33,8 +33,6 @@
 #include <iomanip>
 #include <string>
 
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
 #include <debuggerd/client.h>
 #include <log/log.h>
 #include <utils/misc.h>
@@ -50,10 +48,6 @@
 namespace android
 {
 
-static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
-    return UniqueFile(fopen(path, mode), safeFclose);
-}
-
 enum {
     HEAP_UNKNOWN,
     HEAP_DALVIK,
diff --git a/core/jni/android_os_Debug.h b/core/jni/android_os_Debug.h
index 81270ca..c7b731b 100644
--- a/core/jni/android_os_Debug.h
+++ b/core/jni/android_os_Debug.h
@@ -19,6 +19,8 @@
 
 #include <memory>
 #include <stdio.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 
 namespace android {
 
@@ -27,6 +29,11 @@
 }
 
 using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
+
+inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
+    return UniqueFile(fopen(path, mode), safeFclose);
+}
+
 UniqueFile OpenSmapsOrRollup(int pid);
 
 }  // namespace android
diff --git a/core/jni/android_text_AndroidCharacter.cpp b/core/jni/android_text_AndroidCharacter.cpp
index 8885aac..c6ea4e1 100644
--- a/core/jni/android_text_AndroidCharacter.cpp
+++ b/core/jni/android_text_AndroidCharacter.cpp
@@ -25,9 +25,10 @@
 #include "unicode/uchar.h"
 
 #define PROPERTY_UNDEFINED (-1)
+#define JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY 18
 
 // ICU => JDK mapping
-static int directionality_map[U_CHAR_DIRECTION_COUNT] = {
+static int directionality_map[JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY + 1] = {
     0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0)
     1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1)
     3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3)
@@ -75,7 +76,8 @@
             int c = 0x00010000 + ((src[i] - 0xD800) << 10) +
                                  (src[i + 1] & 0x3FF);
             int dir = u_charDirection(c);
-            if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+            if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+                    || u_charType(c) == U_UNASSIGNED)
                 dir = PROPERTY_UNDEFINED;
             else
                 dir = directionality_map[dir];
@@ -85,7 +87,8 @@
         } else {
             int c = src[i];
             int dir = u_charDirection(c);
-            if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT)
+            if (dir < 0 || dir > JAVA_LANG_CHARACTER_MAX_DIRECTIONALITY
+                    || u_charType(c) == U_UNASSIGNED)
                 dest[i] = PROPERTY_UNDEFINED;
             else
                 dest[i] = directionality_map[dir];
@@ -96,7 +99,7 @@
 static jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input)
 {
     int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH);
-    if (width < 0 || width >= U_EA_COUNT)
+    if (width < 0 || width > u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH))
         width = PROPERTY_UNDEFINED;
 
     return width;
@@ -121,6 +124,7 @@
         return;
     }
 
+    int maxWidth = u_getIntPropertyMaxValue(UCHAR_EAST_ASIAN_WIDTH);
     for (int i = 0; i < count; i++) {
         const int srci = start + i;
         if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF &&
@@ -129,7 +133,7 @@
             int c = 0x00010000 + ((src[srci] - 0xD800) << 10) +
                                  (src[srci + 1] & 0x3FF);
             int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
-            if (width < 0 || width >= U_EA_COUNT)
+            if (width < 0 || width > maxWidth)
                 width = PROPERTY_UNDEFINED;
 
             dest[i++] = width;
@@ -137,7 +141,7 @@
         } else {
             int c = src[srci];
             int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH);
-            if (width < 0 || width >= U_EA_COUNT)
+            if (width < 0 || width > maxWidth)
                 width = PROPERTY_UNDEFINED;
 
             dest[i] = width;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4c7defb..377e65c 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -1128,6 +1128,39 @@
     return pss * 1024;
 }
 
+static jlongArray android_os_Process_getRss(JNIEnv* env, jobject clazz, jint pid)
+{
+    // total, file, anon, swap
+    jlong rss[4] = {0, 0, 0, 0};
+    std::string status_path =
+            android::base::StringPrintf("/proc/%d/status", pid);
+    UniqueFile file = MakeUniqueFile(status_path.c_str(), "re");
+
+    char line[256];
+    while (fgets(line, sizeof(line), file.get())) {
+        jlong v;
+        if ( sscanf(line, "VmRSS: %" SCNd64 " kB", &v) == 1) {
+            rss[0] = v;
+        } else if ( sscanf(line, "RssFile: %" SCNd64 " kB", &v) == 1) {
+            rss[1] = v;
+        } else if ( sscanf(line, "RssAnon: %" SCNd64 " kB", &v) == 1) {
+            rss[2] = v;
+        } else if ( sscanf(line, "VmSwap: %" SCNd64 " kB", &v) == 1) {
+            rss[3] = v;
+        }
+    }
+
+    jlongArray rssArray = env->NewLongArray(4);
+    if (rssArray == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return NULL;
+    }
+
+    env->SetLongArrayRegion(rssArray, 0, 4, rss);
+
+    return rssArray;
+}
+
 jintArray android_os_Process_getPidsForCommands(JNIEnv* env, jobject clazz,
         jobjectArray commandNames)
 {
@@ -1253,6 +1286,7 @@
     {"parseProcLine", "([BII[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_parseProcLine},
     {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
     {"getPss", "(I)J", (void*)android_os_Process_getPss},
+    {"getRss", "(I)[J", (void*)android_os_Process_getRss},
     {"getPidsForCommands", "([Ljava/lang/String;)[I", (void*)android_os_Process_getPidsForCommands},
     //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
     {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index e89b593..752624b 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -468,6 +468,10 @@
     return reinterpret_cast<RenderNode*>(renderNodePtr)->stagingProperties().getAllowForceDark();
 }
 
+static jlong android_view_RenderNode_getUniqueId(jlong renderNodePtr) {
+    return reinterpret_cast<RenderNode*>(renderNodePtr)->uniqueId();
+}
+
 // ----------------------------------------------------------------------------
 // RenderProperties - Animations
 // ----------------------------------------------------------------------------
@@ -694,6 +698,7 @@
     { "nGetHeight",                "(J)I",  (void*) android_view_RenderNode_getHeight },
     { "nSetAllowForceDark",        "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
     { "nGetAllowForceDark",        "(J)Z",  (void*) android_view_RenderNode_getAllowForceDark },
+    { "nGetUniqueId",              "(J)J",  (void*) android_view_RenderNode_getUniqueId },
 };
 
 int register_android_view_RenderNode(JNIEnv* env) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ec9c860..ea6e017 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -377,6 +377,14 @@
     transaction->setCrop_legacy(ctrl, crop);
 }
 
+static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+         jlong nativeObject, jfloat cornerRadius) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
+    transaction->setCornerRadius(ctrl, cornerRadius);
+}
+
 static void nativeSetLayerStack(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint layerStack) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -883,6 +891,8 @@
             (void*)nativeSetFlags },
     {"nativeSetWindowCrop", "(JJIIII)V",
             (void*)nativeSetWindowCrop },
+    {"nativeSetCornerRadius", "(JJF)V",
+            (void*)nativeSetCornerRadius },
     {"nativeSetLayerStack", "(JJI)V",
             (void*)nativeSetLayerStack },
     {"nativeGetBuiltInDisplay", "(I)Landroid/os/IBinder;",
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 7de8020..f22d57f 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -550,6 +550,10 @@
     }
     optional MultiSim multi_sim = 76;
 
+    // Whether we've enabled native flags health check on this device. Takes effect on
+    // reboot. The value "1" enables native flags health check; otherwise it's disabled.
+    optional SettingProto native_flags_health_check_enabled = 144 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message Netstats {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -865,6 +869,8 @@
         // Temperature at which the high temperature warning notification should
         // be shown.
         optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // USB temperature at which the high temperature alarm notification should be shown.
+        optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional TemperatureWarning temperature_warning = 119;
 
@@ -987,5 +993,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 144;
+    // Next tag = 145;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6e661e1..0e052fe 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -234,6 +234,18 @@
     }
     optional Location location = 31;
 
+    // How frequently will the user be reminded about location permission grants
+    message LocationAccessCheck {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Time in between periodic checks
+        optional SettingProto interval_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+        // Time in between the user granting a location permission and a check
+        optional SettingProto delay_millis = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional LocationAccessCheck location_access_check = 73;
+
     message LockScreen {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -515,5 +527,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 73;
+    // Next tag = 74;
 }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 99f096d..3767ed5 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,7 +156,7 @@
     optional int32 rotation = 11;
     optional ScreenRotationAnimationProto screen_rotation_animation = 12;
     optional DisplayFramesProto display_frames = 13;
-    optional int32 surface_size = 14;
+    optional int32 surface_size = 14 [deprecated=true];
     optional string focused_app = 15;
     optional AppTransitionProto app_transition = 16;
 }
@@ -211,7 +211,7 @@
     repeated AppWindowTokenProto app_window_tokens = 3;
     optional bool fills_parent = 4;
     optional .android.graphics.RectProto bounds = 5;
-    optional .android.graphics.RectProto temp_inset_bounds = 6;
+    optional .android.graphics.RectProto displayed_bounds = 6;
     optional bool defer_removal = 7;
     optional int32 surface_width = 8;
     optional int32 surface_height = 9;
diff --git a/core/proto/android/service/runtime.proto b/core/proto/android/service/runtime.proto
new file mode 100644
index 0000000..ecbccef
--- /dev/null
+++ b/core/proto/android/service/runtime.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+package android.service.runtime;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "RuntimeServiceProto";
+
+// Represents dumpsys info from RuntimeService.
+message RuntimeServiceInfoProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  // Generic debug information to include.
+  repeated DebugEntryProto debug_entry = 1;
+}
+
+// A piece of key / value debug information.
+message DebugEntryProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional string key = 1;
+
+  optional string string_value = 2;
+}
diff --git a/core/proto/android/stats/devicepolicy/Android.bp b/core/proto/android/stats/devicepolicy/Android.bp
new file mode 100644
index 0000000..6ae54e2
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2018 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.
+
+java_library_static {
+    name: "devicepolicyprotosnano",
+    proto: {
+        type: "nano",
+    },
+    srcs: [
+        "*.proto",
+    ],
+    java_version: "1.8",
+    target: {
+        android: {
+            jarjar_rules: "jarjar-rules.txt",
+        },
+        host: {
+            static_libs: ["libprotobuf-java-nano"],
+        }
+    },
+    no_framework_libs: true,
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/proto/android/stats/devicepolicy/device_policy.proto
similarity index 72%
rename from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
rename to core/proto/android/stats/devicepolicy/device_policy.proto
index 27d25b8..af30cf3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/proto/android/stats/devicepolicy/device_policy.proto
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+syntax = "proto2";
+
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+message StringList {
+  repeated string string_value = 1;
 }
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
new file mode 100644
index 0000000..8fbea12
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+
+package android.stats.devicepolicy;
+option java_multiple_files = true;
+
+/**
+ * Id for device policy features.
+ */
+enum EventId {
+  SET_PASSWORD_QUALITY = 1;
+  SET_PASSWORD_MINIMUM_LENGTH = 2;
+  SET_PASSWORD_MINIMUM_NUMERIC = 3;
+  SET_PASSWORD_MINIMUM_NON_LETTER = 4;
+  SET_PASSWORD_MINIMUM_LETTERS = 5;
+  SET_PASSWORD_MINIMUM_LOWER_CASE = 6;
+  SET_PASSWORD_MINIMUM_UPPER_CASE = 7;
+  SET_PASSWORD_MINIMUM_SYMBOLS = 8;
+  SET_KEYGUARD_DISABLED_FEATURES = 9;
+  LOCK_NOW = 10;
+  WIPE_DATA_WITH_REASON = 11;
+  ADD_USER_RESTRICTION = 12;
+  REMOVE_USER_RESTRICTION = 13;
+  SET_SECURE_SETTING = 14;
+  SET_SECURITY_LOGGING_ENABLED = 15;
+  RETRIEVE_SECURITY_LOGS = 16;
+  RETRIEVE_PRE_REBOOT_SECURITY_LOGS = 17;
+  SET_PERMISSION_POLICY = 18;
+  SET_PERMISSION_GRANT_STATE = 19;
+  INSTALL_KEY_PAIR = 20;
+  INSTALL_CA_CERT = 21;
+  ON_CHOOSE_KEY_ALIAS = 22;
+  REMOVE_KEY_PAIR = 23;
+  UNINSTALL_CA_CERTS = 24;
+  SET_CERT_INSTALLER_PACKAGE = 25;
+  SET_ALWAYS_ON_VPN_PACKAGE = 26;
+  SET_PERMITTED_INPUT_METHODS = 27;
+  SET_PERMITTED_ACCESSIBILITY_SERVICES = 28;
+  SET_SCREEN_CAPTURE_DISABLE = 29;
+  SET_CAMERA_DISABLED = 30;
+  QUERY_SUMMARY_FOR_USER = 31;
+  QUERY_SUMMARY = 32;
+  QUERY_DETAILS = 33;
+  REBOOT = 34;
+  SET_MASTER_VOLUME_MUTED = 35;
+  SET_AUTO_TIME_REQUIRED = 36;
+  SET_KEYGUARD_DISABLED = 37;
+  SET_STATUS_BAR_DISABLED = 38;
+  SET_ORGANIZATION_COLOR = 39;
+  SET_PROFILE_NAME = 40;
+  SET_USER_ICON = 41;
+  SET_DEVICE_OWNER_LOCKSCREEN_INFO = 42;
+  SET_SHORT_SUPPORT_MESSAGE = 43;
+  SET_LONG_SUPPORT_MESSAGE = 44;
+  SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED = 45;
+  SET_CROSS_PROFILE_CALLER_DISABLED = 46;
+  SET_BLUETOOTH_CONTACT_SHARING_DISABLED = 47;
+  ADD_CROSS_PROFILE_INTENT_FILTER = 48;
+  ADD_CROSS_PROFILE_WIDGET_PROVIDER = 49;
+  SET_SYSTEM_UPDATE_POLICY = 50;
+  SET_LOCKTASK_PACKAGES = 51;
+  ADD_PERSISTENT_PREFERRED_ACTIVITY = 52;
+  REQUEST_BUGREPORT = 53;
+  GET_WIFI_MAC_ADDRESS = 54;
+  REQUEST_QUIET_MODE_ENABLED = 55;
+  WORK_PROFILE_LOCATION_CHANGED = 56;
+  DO_USER_INFO_CLICKED = 57;
+  TRANSFER_OWNERSHIP = 58;
+  GENERATE_KEY_PAIR = 59;
+  SET_KEY_PAIR_CERTIFICATE = 60;
+  SET_KEEP_UNINSTALLED_PACKAGES = 61;
+  SET_APPLICATION_RESTRICTIONS = 62;
+  SET_APPLICATION_HIDDEN = 63;
+  ENABLE_SYSTEM_APP = 64;
+  ENABLE_SYSTEM_APP_WITH_INTENT = 65;
+  INSTALL_EXISTING_PACKAGE = 66;
+  SET_UNINSTALL_BLOCKED = 67;
+  SET_PACKAGES_SUSPENDED = 68;
+  ON_LOCK_TASK_MODE_ENTERING = 69;
+  ADD_CROSS_PROFILE_CALENDAR_PACKAGE = 70;
+  REMOVE_CROSS_PROFILE_CALENDAR_PACKAGE = 71;
+  GET_USER_PASSWORD_COMPLEXITY_LEVEL = 72;
+  INSTALL_SYSTEM_UPDATE = 73;
+  INSTALL_SYSTEM_UPDATE_ERROR = 74;
+  IS_MANAGED_KIOSK = 75;
+  IS_UNATTENDED_MANAGED_KIOSK = 76;
+  PROVISIONING_TO_COMP = 77;
+  PROVISIONING_FORCED_DO = 78;
+
+  // existing Tron logs to be migrated to WestWorld
+  PROVISIONING_ENTRY_POINT_NFC = 79;
+  PROVISIONING_ENTRY_POINT_QR_CODE = 80;
+  PROVISIONING_ENTRY_POINT_ZERO_TOUCH = 81;
+  PROVISIONING_ENTRY_POINT_ADB = 82;
+  PROVISIONING_ENTRY_POINT_TRUSTED_SOURCE = 83;
+  PROVISIONING_DPC_PACKAGE_NAME = 84;
+  PROVISIONING_DPC_INSTALLED_BY_PACKAGE = 85;
+  PROVISIONING_PROVISIONING_ACTIVITY_TIME_MS = 86;
+  PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
+  PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
+  PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
+  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
+  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
+  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+  PROVISIONING_NETWORK_TYPE = 93;
+  PROVISIONING_ACTION = 94;
+  PROVISIONING_EXTRAS = 95;
+  PROVISIONING_COPY_ACCOUNT_TASK_MS = 96;
+  PROVISIONING_CREATE_PROFILE_TASK_MS = 97;
+  PROVISIONING_START_PROFILE_TASK_MS = 98;
+  PROVISIONING_DOWNLOAD_PACKAGE_TASK_MS = 99;
+  PROVISIONING_INSTALL_PACKAGE_TASK_MS = 100;
+  PROVISIONING_CANCELLED = 101;
+  PROVISIONING_ERROR = 102;
+  PROVISIONING_COPY_ACCOUNT_STATUS = 103;
+  PROVISIONING_TOTAL_TASK_TIME_MS = 104;
+  PROVISIONING_SESSION_STARTED = 105;
+  PROVISIONING_SESSION_COMPLETED = 106;
+  PROVISIONING_TERMS_ACTIVITY_TIME_MS = 107;
+  PROVISIONING_TERMS_COUNT = 108;
+  PROVISIONING_TERMS_READ = 109;
+}
diff --git a/core/proto/android/stats/devicepolicy/jarjar-rules.txt b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
new file mode 100644
index 0000000..40043a86
--- /dev/null
+++ b/core/proto/android/stats/devicepolicy/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index fba2e51..4777169 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -51,6 +51,7 @@
     NETWORK_TYPE_TD_SCDMA = 17;
     NETWORK_TYPE_IWLAN = 18;
     NETWORK_TYPE_LTE_CA = 19;
+    NETWORK_TYPE_NR = 20;
 }
 
 // Signal strength levels, primarily used by android/telephony/SignalStrength.java.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6fecb68..df17b4c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -620,11 +620,20 @@
 
     <protected-broadcast android:name="android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL" />
 
+    <protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
     <eat-comment />
 
+    <!-- Grouping for platform runtime permissions is not accessible to apps
+         @hide
+         @SystemApi
+    -->
+    <permission-group android:name="android.permission-group.UNDEFINED"
+        android:priority="100" />
+
     <!-- ====================================================================== -->
     <!-- Permissions for accessing user's contacts including personal profile   -->
     <!-- ====================================================================== -->
@@ -643,14 +652,17 @@
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CONTACTS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readContacts"
         android:description="@string/permdesc_readContacts"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to write the user's contacts data.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CONTACTS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_writeContacts"
         android:description="@string/permdesc_writeContacts"
         android:protectionLevel="dangerous" />
@@ -672,14 +684,17 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CALENDAR"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readCalendar"
         android:description="@string/permdesc_readCalendar"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to write the user's calendar data.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CALENDAR"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_writeCalendar"
         android:description="@string/permdesc_writeCalendar"
         android:protectionLevel="dangerous" />
@@ -701,6 +716,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.SEND_SMS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_sendSms"
         android:description="@string/permdesc_sendSms"
         android:permissionFlags="costsMoney"
@@ -710,33 +726,41 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_SMS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_receiveSms"
         android:description="@string/permdesc_receiveSms"
-        android:protectionLevel="dangerous"/>
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to read SMS messages.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_SMS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readSms"
         android:description="@string/permdesc_readSms"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to receive WAP push messages.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_WAP_PUSH"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_receiveWapPush"
         android:description="@string/permdesc_receiveWapPush"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to monitor incoming MMS messages.
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECEIVE_MMS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_receiveMms"
         android:description="@string/permdesc_receiveMms"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- @SystemApi @TestApi Allows an application to read previously received cell broadcast
          messages and to register a content observer to get notifications when
@@ -751,9 +775,11 @@
          <p>Protection level: dangerous
          @hide Pending API council approval -->
     <permission android:name="android.permission.READ_CELL_BROADCASTS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readCellBroadcasts"
         android:description="@string/permdesc_readCellBroadcasts"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing external storage                             -->
@@ -792,6 +818,7 @@
      @deprecated replaced by new strongly-typed permission groups in Q.
      -->
     <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_sdcardRead"
         android:description="@string/permdesc_sdcardRead"
         android:protectionLevel="dangerous"
@@ -813,6 +840,7 @@
          @deprecated replaced by new strongly-typed permission groups in Q.
     -->
     <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_sdcardWrite"
         android:description="@string/permdesc_sdcardWrite"
         android:protectionLevel="dangerous"
@@ -829,9 +857,11 @@
 
     <!-- Allows an application to read the user's shared audio collection. -->
     <permission android:name="android.permission.READ_MEDIA_AUDIO"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_audioRead"
         android:description="@string/permdesc_audioRead"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Runtime permission controlling access to the user's shared visual media
          collection, including images and videos. -->
@@ -844,22 +874,28 @@
 
     <!-- Allows an application to read the user's shared images collection. -->
     <permission android:name="android.permission.READ_MEDIA_IMAGES"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_imagesRead"
         android:description="@string/permdesc_imagesRead"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to read the user's shared video collection. -->
     <permission android:name="android.permission.READ_MEDIA_VIDEO"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_videoRead"
         android:description="@string/permdesc_videoRead"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to access any geographic locations persisted in the
          user's shared collection. -->
     <permission android:name="android.permission.ACCESS_MEDIA_LOCATION"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_mediaLocation"
         android:description="@string/permdesc_mediaLocation"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- @hide @SystemApi
          Allows an application to modify OBB files visible to other apps. -->
@@ -887,20 +923,24 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_FINE_LOCATION"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_accessFineLocation"
         android:description="@string/permdesc_accessFineLocation"
         android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an app to access approximate location.
          Alternatively, you might want {@link #ACCESS_FINE_LOCATION}.
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_accessCoarseLocation"
         android:description="@string/permdesc_accessCoarseLocation"
         android:backgroundPermission="android.permission.ACCESS_BACKGROUND_LOCATION"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an app to access location in the background.  If you
          are requesting this, you should also request {@link #ACCESS_FINE_LOCATION}.
@@ -909,9 +949,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_accessBackgroundLocation"
         android:description="@string/permdesc_accessBackgroundLocation"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the call log                                 -->
@@ -949,9 +991,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_CALL_LOG"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readCallLog"
         android:description="@string/permdesc_readCallLog"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to write (but not read) the user's
          call log data.
@@ -967,6 +1011,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.WRITE_CALL_LOG"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_writeCallLog"
         android:description="@string/permdesc_writeCallLog"
         android:protectionLevel="dangerous" />
@@ -977,9 +1022,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_processOutgoingCalls"
         android:description="@string/permdesc_processOutgoingCalls"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device telephony                         -->
@@ -1008,23 +1055,28 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.READ_PHONE_STATE"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readPhoneState"
         android:description="@string/permdesc_readPhoneState"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows read access to the device's phone number(s). This is a subset of the capabilities
          granted by {@link #READ_PHONE_STATE} but is exposed to instant applications.
          <p>Protection level: dangerous-->
     <permission android:name="android.permission.READ_PHONE_NUMBERS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_readPhoneNumbers"
         android:description="@string/permdesc_readPhoneNumbers"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an application to initiate a phone call without going through
         the Dialer user interface for the user to confirm the call.
         <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.CALL_PHONE"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:permissionFlags="costsMoney"
         android:label="@string/permlab_callPhone"
         android:description="@string/permdesc_callPhone"
@@ -1034,6 +1086,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_addVoicemail"
         android:description="@string/permdesc_addVoicemail"
         android:protectionLevel="dangerous" />
@@ -1042,6 +1095,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.USE_SIP"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:description="@string/permdesc_use_sip"
         android:label="@string/permlab_use_sip"
         android:protectionLevel="dangerous"/>
@@ -1050,6 +1104,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ANSWER_PHONE_CALLS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_answerPhoneCalls"
         android:description="@string/permdesc_answerPhoneCalls"
         android:protectionLevel="dangerous|runtime" />
@@ -1077,6 +1132,7 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACCEPT_HANDOVER"
+                android:permissionGroup="android.permission-group.UNDEFINED"
                 android.label="@string/permlab_acceptHandover"
                 android:description="@string/permdesc_acceptHandovers"
                 android:protectionLevel="dangerous" />
@@ -1100,9 +1156,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.RECORD_AUDIO"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_recordAudio"
         android:description="@string/permdesc_recordAudio"
-        android:protectionLevel="dangerous|instant"/>
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for activity recognition                        -->
@@ -1121,9 +1179,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.ACTIVITY_RECOGNITION"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_activityRecognition"
         android:description="@string/permdesc_activityRecognition"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the UCE Service                              -->
@@ -1169,9 +1229,11 @@
          <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.CAMERA"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_camera"
         android:description="@string/permdesc_camera"
-        android:protectionLevel="dangerous|instant" />
+        android:protectionLevel="dangerous|instant"
+        android:usageInfoRequired="true" />
 
 
     <!-- ====================================================================== -->
@@ -1192,9 +1254,11 @@
          measure what is happening inside his/her body, such as heart rate.
          <p>Protection level: dangerous -->
     <permission android:name="android.permission.BODY_SENSORS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_bodySensors"
         android:description="@string/permdesc_bodySensors"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous"
+        android:usageInfoRequired="true" />
 
     <!-- Allows an app to use fingerprint hardware.
          <p>Protection level: normal
@@ -1678,9 +1742,11 @@
     <p>Protection level: dangerous
     -->
     <permission android:name="android.permission.GET_ACCOUNTS"
+        android:permissionGroup="android.permission-group.UNDEFINED"
         android:protectionLevel="dangerous"
         android:description="@string/permdesc_getAccounts"
-        android:label="@string/permlab_getAccounts" />
+        android:label="@string/permlab_getAccounts"
+        android:usageInfoRequired="true" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
 
     <!-- @SystemApi Allows applications to call into AccountAuthenticators.
@@ -2981,12 +3047,12 @@
     <permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
                 android:protectionLevel="signature" />
 
-    <!-- Must be required by a android.service.intelligence.IntelligenceService,
+    <!-- Must be required by a android.service.intelligence.SmartSuggestionsService,
          to ensure that only the system can bind to it.
          @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
          <p>Protection level: signature
     -->
-    <permission android:name="android.permission.BIND_INTELLIGENCE_SERVICE"
+    <permission android:name="android.permission.BIND_SMART_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
     <!-- Must be required by hotword enrollment application,
@@ -3008,6 +3074,13 @@
     <permission android:name="android.permission.BIND_TV_INPUT"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by an {@link android.service.sms.FinancialSmsService}
+         to ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_FINANCIAL_SMS_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- @SystemApi
          Must be required by a {@link com.android.media.tv.remoteprovider.TvRemoteProvider}
          to ensure that only the system can bind to it.
@@ -4097,6 +4170,11 @@
     <permission android:name="android.permission.MANAGE_AUTO_FILL"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the smart suggestions service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS"
+        android:protectionLevel="signature" />
+
     <!-- Allows an app to set the theme overlay in /vendor/overlay
          being used.
          @hide  <p>Not for use by third-party applications.</p> -->
@@ -4197,6 +4275,28 @@
     <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS"
         android:protectionLevel="signature|appop" />
 
+    <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#P} that want to use
+         {@link android.app.Notification.Builder#setFullScreenIntent notification full screen
+         intents}.  -->
+    <permission android:name="android.permission.USE_FULL_SCREEN_INTENT"
+                android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows requesting the framework broadcast the
+         {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY} intent.
+         @hide -->
+    <permission android:name="android.permission.SEND_DEVICE_CUSTOMIZATION_READY"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Permission that protects the {@link Intent#ACTION_DEVICE_CUSTOMIZATION_READY}
+         intent.
+         @hide -->
+    <permission android:name="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY"
+        android:protectionLevel="signature|preinstalled" />
+    <!-- @SystemApi Allows wallpaper to be rendered in ambient mode.
+         @hide -->
+    <permission android:name="android.permission.AMBIENT_WALLPAPER"
+                android:protectionLevel="signature|preinstalled" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
@@ -4516,6 +4616,13 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.WallpaperUpdateReceiver"
+                  android:permission="android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY">
+            <intent-filter>
+                <action android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY"/>
+            </intent-filter>
+        </receiver>
+
         <service android:name="android.hardware.location.GeofenceHardwareService"
             android:permission="android.permission.LOCATION_HARDWARE"
             android:exported="false" />
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 433ae39..4bf1ad6 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -126,7 +126,6 @@
         android:visibility="gone"
         android:contentDescription="@string/notification_alerted_content_description"
         android:src="@drawable/ic_notifications_alerted"
-        android:tint="@color/notification_secondary_text_color_light"
     />
     <ImageView
         android:id="@+id/profile_badge"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6c4861b..f55e48e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1915,6 +1915,9 @@
         <enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
         <enum name="KEYCODE_ALL_APPS" value="284" />
         <enum name="KEYCODE_REFRESH" value="285" />
+        <enum name="KEYCODE_THUMBS_UP" value="286" />
+        <enum name="KEYCODE_THUMBS_DOWN" value="287" />
+        <enum name="KEYCODE_PROFILE_SWITCH" value="288" />
     </attr>
 
     <!-- ***************************************************************** -->
@@ -5532,7 +5535,7 @@
         <attr name="solidColor" format="color|reference" />
         <!-- @hide The divider for making the selection area. -->
         <attr name="selectionDivider" format="reference" />
-        <!-- @hide The height of the selection divider. -->
+        <!-- The height of the selection divider. -->
         <attr name="selectionDividerHeight" format="dimension" />
         <!-- @hide The distance between the two selection dividers. -->
         <attr name="selectionDividersDistance" format="dimension" />
@@ -7935,7 +7938,9 @@
              wallpaper. -->
         <attr name="showMetadataInPreview" format="boolean" />
 
-        <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
+        <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
+             This feature requires the android.permission.AMBIENT_WALLPAPER permission.
+             @hide @SystemApi -->
         <attr name="supportsAmbientMode" format="boolean" />
 
         <!-- Uri that specifies a settings Slice for this wallpaper. -->
@@ -8803,6 +8808,7 @@
         <attr name="layout_ignoreOffset" format="boolean" />
         <attr name="layout_gravity" />
         <attr name="layout_hasNestedScrollIndicator" format="boolean" />
+        <attr name="layout_maxHeight" />
     </declare-styleable>
 
     <!-- @hide -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6fc0f5b..089c59f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1601,6 +1601,10 @@
         <attr name="request" />
         <attr name="protectionLevel" />
         <attr name="permissionFlags" />
+        <!-- If {@code true} applications that target Q <em>must</em> specify the permission usage
+             attributes in their {@code uses-permission} elements or the permission will not be
+             granted. -->
+        <attr name="usageInfoRequired" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>permission-group</code> tag declares a logical grouping of
@@ -1700,6 +1704,81 @@
         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" />
+
+        <!-- Specify if the app uploads data, or derived data, guarded by this permission.
+
+             If the permission is defined with {@link android.R.attr#usageInfoRequired}
+             {@code true} this <em>must</em> be specified by apps that target Android Q or the
+             permission will not be granted, it will be as if the manifest didn't request it at all.
+        -->
+        <attr name="dataSentOffDevice">
+          <!-- The application may send data, or derived data, guarded by this permission off of the
+               device. -->
+          <enum name="yes" value="1" />
+          <!-- The application may send data, or derived data, guarded by this permission off of the
+               device, however it will only do so when explicitly triggered by a user action. -->
+          <enum name="userTriggered" value="2" />
+          <!-- The application does not send data, or derived data, guarded by this permission off
+               of the device. -->
+          <enum name="no" value="3" />
+        </attr>
+
+        <!-- Specify if the application or its related off-device services provide data,
+             or derived data, guarded by this permission to third parties outside of the developer's
+             organization that do not qualify as data processors.
+
+             If the permission is defined with {@link android.R.attr#usageInfoRequired}
+             {@code true} this <em>must</em> be specified by apps that target Android Q or the
+             permission will not be granted, it will be as if the manifest didn't request it at all.
+             -->
+        <attr name="dataSharedWithThirdParty">
+          <!-- The application or its services may provide data, or derived data, guarded by this
+               permission to third party organizations. -->
+          <enum name="yes" value="1" />
+          <!-- The application or its services may provide data, or derived data, guarded by this
+               permission to third party organizations, however it will only do so when explicitly
+               triggered by a user action. -->
+          <enum name="userTriggered" value="2" />
+          <!-- The application or its services does not provide data, or derived data, guarded by
+               this permission to third party organizations. -->
+          <enum name="no" value="3" />
+        </attr>
+
+        <!-- Specify if the application or its related off-device services use data,
+             or derived data, guarded by this permission for monetization purposes.
+
+             For example, if the data is sold to another party or used for targeting advertisements
+             this must be set to {@code yes}.
+
+             If the permission is defined with {@link android.R.attr#usageInfoRequired}
+             {@code true} this <em>must</em> be specified by apps that target Android Q or the
+             permission will not be granted, it will be as if the manifest didn't request it at all.
+             -->
+        <attr name="dataUsedForMonetization">
+          <!-- The application or its services may use data, or derived data, guarded by this
+               permission for monetization purposes. -->
+          <enum name="yes" value="1" />
+          <!-- The application or its services may use data, or derived data, guarded by this
+               permission for monetization purposes, however it will only do so when explicity
+               triggered by a user action. -->
+          <enum name="userTriggered" value="2" />
+          <!--  The application or its services does not use data, or derived data, guarded by
+                this permission for monetization purposes. -->
+          <enum name="no" value="3" />
+        </attr>
+
+        <!-- Specify how long the application or its related off-device services store
+             data, or derived data, guarded by this permission.
+
+             This can be one of "notRetained", "userSelected", "unlimited", or a number
+             representing the number of weeks the data is retained.
+
+             If the permission is defined with {@link android.R.attr#usageInfoRequired}
+             {@code true} this <em>must</em> be specified by apps that target Android Q or the
+             permission will not be granted, it will be as if the manifest didn't request it at all.
+             -->
+        <attr name="dataRetentionTime" format="string" />
+
     </declare-styleable>
 
     <!-- The <code>uses-configuration</code> tag specifies
@@ -2148,6 +2227,21 @@
         <attr name="visibleToInstantApps" />
         <!-- The code for this component is located in the given split. -->
         <attr name="splitName" />
+        <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
+             will be spawned from an Application Zygote, instead of the regular Zygote.
+             <p>
+             The Application Zygote will pre-initialize the application's class loader,
+             and call a static callback into the application to allow it to perform
+             application-specific preloads (such as loading a shared library). Therefore,
+             spawning from the Application Zygote will typically reduce the service
+             launch time and reduce its memory usage. The downside of using this flag
+             is that you will have an additional process (the app zygote itself) that
+             is taking up memory. Whether actual memory usage is improved therefore strongly
+             depends on the number of isolated services that an application starts,
+             and how much memory those services save by preloading. Therefore, it is
+             recommended to measure memory usage under typical workloads to determine
+             whether it makes sense to use this flag. -->
+        <attr name="useAppZygote" format="boolean" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1d80961..0cd6bc5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2125,6 +2125,9 @@
          during initialization when the setting is still null. -->
     <bool name="config_dozeAlwaysOnEnabled">true</bool>
 
+    <!-- If AOD can show an ambient version of the wallpaper -->
+    <bool name="config_dozeSupportsAodWallpaper">true</bool>
+
     <!-- Whether the display blanks itself when transitioning from a doze to a non-doze state -->
     <bool name="config_displayBlanksAfterDoze">false</bool>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index f7b9961..05a156b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -430,7 +430,7 @@
     <dimen name="notification_badge_size">12dp</dimen>
 
     <!-- Size of the alerted icon for notifications -->
-    <dimen name="notification_alerted_size">18dp</dimen>
+    <dimen name="notification_alerted_size">12dp</dimen>
 
     <!-- Keyguard dimensions -->
     <!-- TEMP -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 64e5bc0..bbe3ff9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -190,4 +190,7 @@
 
   <!-- A tag used to save the view added to a transition overlay -->
   <item type="id" name="transition_overlay_view_tag" />
+
+  <!-- A tag used to save the notification action object -->
+  <item type="id" name="notification_action_index_tag" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3373d14..5e8af62 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2909,6 +2909,7 @@
         <public name="opticalInsetRight" />
         <public name="opticalInsetBottom" />
         <public name="forceDarkAllowed" />
+        <!-- @hide @SystemApi -->
         <public name="supportsAmbientMode" />
         <!-- @hide For use by platform and tools only. Developers should not specify this value. -->
         <public name="usesNonSdkApi" />
@@ -2921,6 +2922,13 @@
         <public name="interactiveUiTimeout" />
         <public name="importantForContentCapture" />
         <public name="supportsMultipleDisplays" />
+        <public name="useAppZygote" />
+        <public name="usageInfoRequired" />
+        <public name="dataSentOffDevice" />
+        <public name="dataSharedWithThirdParty" />
+        <public name="dataUsedForMonetization" />
+        <public name="dataRetentionTime" />
+        <public name="selectionDividerHeight" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b4">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index af291c3..a33f6b2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1444,9 +1444,14 @@
 
     <!-- Title shown when the system-provided biometric dialog is shown, asking the user to authenticate. [CHAR LIMIT=40] -->
     <string name="biometric_dialog_default_title">Application <xliff:g id="app" example="Gmail">%s</xliff:g> wants to authenticate.</string>
-
     <!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
     <string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
+    <!-- Message shown when biometric authentication was canceled by the user [CHAR LIMIT=50] -->
+    <string name="biometric_error_user_canceled">Authentication canceled</string>
+    <!-- Message shown by the biometric dialog when biometric is not recognized -->
+    <string name="biometric_not_recognized">Not recognized</string>
+    <!-- Message shown when biometric authentication has been canceled [CHAR LIMIT=50] -->
+    <string name="biometric_error_canceled">Authentication canceled</string>
 
     <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
     <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
@@ -1462,8 +1467,6 @@
     <string-array name="fingerprint_acquired_vendor">
     </string-array>
 
-    <!-- Message shown by the biometric dialog when biometric is not recognized -->
-    <string name="biometric_not_recognized">Not recognized</string>
     <!-- Accessibility message announced when a fingerprint has been authenticated [CHAR LIMIT=NONE] -->
     <string name="fingerprint_authenticated">Fingerprint authenticated</string>
     <!-- Accessibility message announced when a face has been authenticated [CHAR LIMIT=NONE] -->
@@ -1585,23 +1588,15 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readSyncStats">Allows an app to read the sync stats for an account, including the history of sync events and how much data is synced. </string>
 
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardRead" product="nosdcard">read the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardRead" product="default">read the contents of your SD card</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardRead" product="nosdcard">Allows the app to read the contents of your USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardRead" product="default">Allows the app to read the contents of your SD card.</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permlab_sdcardRead">read the contents of your shared storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+    <string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
 
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permlab_sdcardWrite" product="nosdcard">modify or delete the contents of your USB storage</string>
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_sdcardWrite" product="default">modify or delete the contents of your SD card</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] -->
-    <string name="permdesc_sdcardWrite" product="nosdcard">Allows the app to write to the USB storage.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_sdcardWrite" product="default">Allows the app to write to the SD card.</string>
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
+    <string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
+    <string name="permdesc_sdcardWrite">Allows the app to write the contents of your shared storage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_use_sip">make/receive SIP calls</string>
@@ -2995,6 +2990,12 @@
     <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] -->
     <string name="translate_desc">Translate selected text</string>
 
+    <!-- Label for item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=30] -->
+    <string name="define">Define</string>
+
+    <!-- Accessibility description for an item in the text selection menu to define selected text with a dictionary app. Should be a verb. [CHAR LIMIT=NONE] -->
+    <string name="define_desc">Define selected text</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7a78327..01422c8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -576,6 +576,8 @@
   <java-symbol type="string" name="view_flight_desc" />
   <java-symbol type="string" name="translate" />
   <java-symbol type="string" name="translate_desc" />
+  <java-symbol type="string" name="define" />
+  <java-symbol type="string" name="define_desc" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -2404,7 +2406,9 @@
   <!-- Biometric messages -->
   <java-symbol type="string" name="biometric_dialog_default_title" />
   <java-symbol type="string" name="biometric_error_hw_unavailable" />
+  <java-symbol type="string" name="biometric_error_user_canceled" />
   <java-symbol type="string" name="biometric_not_recognized" />
+  <java-symbol type="string" name="biometric_error_canceled" />
 
   <!-- Fingerprint messages -->
   <java-symbol type="string" name="fingerprint_error_unable_to_process" />
@@ -2681,6 +2685,7 @@
   <java-symbol type="id" name="smart_reply_container" />
   <java-symbol type="id" name="remote_input_tag" />
   <java-symbol type="id" name="pending_intent_tag" />
+  <java-symbol type="id" name="notification_action_index_tag" />
 
   <java-symbol type="attr" name="seekBarDialogPreferenceStyle" />
   <java-symbol type="string" name="ext_media_status_removed" />
@@ -3305,6 +3310,7 @@
   <java-symbol type="integer" name="config_autoGroupAtCount" />
   <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
   <java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
+  <java-symbol type="bool" name="config_dozeSupportsAodWallpaper" />
   <java-symbol type="bool" name="config_displayBlanksAfterDoze" />
   <java-symbol type="bool" name="config_displayBrightnessBucketsInDoze" />
   <java-symbol type="integer" name="config_storageManagerDaystoRetainDefault" />
@@ -3496,4 +3502,8 @@
   <java-symbol type="string" name="config_defaultAssistantComponentName" />
 
   <java-symbol type="id" name="transition_overlay_view_tag" />
+
+  <java-symbol type="dimen" name="rounded_corner_radius" />
+  <java-symbol type="dimen" name="rounded_corner_radius_top" />
+  <java-symbol type="dimen" name="rounded_corner_radius_bottom" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index f60d8d0ad..46d4a47 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -91,6 +91,8 @@
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.KILL_UID" />
 
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+
     <!-- location test permissions -->
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
@@ -1316,6 +1318,12 @@
         <service android:name="android.os.BinderThreadPriorityService"
                 android:process=":BinderThreadPriorityService" />
 
+        <!-- Used by BinderWorkSourceTest -->
+        <service android:name="android.os.BinderWorkSourceService"
+                android:process=":BinderWorkSourceService" />
+        <service android:name="android.os.BinderWorkSourceNestedService"
+                android:process=":BinderWorkSourceNestedService" />
+
         <!-- Application components used for search manager tests -->
 
         <activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e1cb911..e89a4d3 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -106,9 +106,9 @@
         int backgroundColor = 0xff585868;
         int initialForegroundColor = 0xff505868;
         builder.setColorPalette(backgroundColor, initialForegroundColor);
-        int primaryTextColor = builder.getPrimaryTextColor();
+        int primaryTextColor = builder.getPrimaryTextColor(builder.mParams);
         assertTrue(satisfiesTextContrast(primaryTextColor, backgroundColor));
-        int secondaryTextColor = builder.getSecondaryTextColor();
+        int secondaryTextColor = builder.getSecondaryTextColor(builder.mParams);
         assertTrue(satisfiesTextContrast(secondaryTextColor, backgroundColor));
     }
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index a317c99..8ac9451d 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -364,9 +364,7 @@
 
     private int getInstallLoc(int flags, int expInstallLocation, long pkgLen) {
         // Flags explicitly over ride everything else.
-        if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) {
-            return INSTALL_LOC_SD;
-        } else if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
+        if ((flags & PackageManager.INSTALL_INTERNAL) != 0) {
             return INSTALL_LOC_INT;
         }
         // Manifest option takes precedence next
@@ -437,8 +435,6 @@
 
             int rLoc = getInstallLoc(flags, expInstallLocation, pkgLen);
             if (rLoc == INSTALL_LOC_INT) {
-                assertFalse(
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertEquals(appInstallPath, srcPath);
                 assertEquals(appInstallPath, publicSrcPath);
                 assertStartsWith("Native library should point to shared lib directory",
@@ -461,8 +457,6 @@
                     }
                 }
             } else if (rLoc == INSTALL_LOC_SD) {
-                assertFalse("The application should not be installed forward locked",
-                        (info.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0);
                 assertTrue("Application flags (" + info.flags
                         + ") should contain FLAG_EXTERNAL_STORAGE",
                         (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -845,31 +839,10 @@
     }
 
     @LargeTest
-    public void testReplaceFailSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_EXTERNAL);
-    }
-
-    @LargeTest
     public void testReplaceNormalInternal() throws Exception {
         sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
     }
 
-    @LargeTest
-    public void testReplaceSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleReplaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING
-                | PackageManager.INSTALL_EXTERNAL);
-    }
-
     /* -------------- Delete tests --- */
     private static class DeleteObserver extends IPackageDeleteObserver.Stub {
         private CountDownLatch mLatch = new CountDownLatch(1);
@@ -1015,31 +988,12 @@
         deleteFromRawResource(0, 0);
     }
 
-    @LargeTest
-    public void testDeleteSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, 0);
-    }
 
     @LargeTest
     public void testDeleteNormalInternalRetainData() throws Exception {
         deleteFromRawResource(0, PackageManager.DELETE_KEEP_DATA);
     }
 
-    @LargeTest
-    public void testDeleteSdcardRetainData() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        deleteFromRawResource(PackageManager.INSTALL_EXTERNAL, PackageManager.DELETE_KEEP_DATA);
-    }
-
     void cleanUpInstall(InstallParams ip) throws Exception {
         if (ip == null) {
             return;
@@ -1104,60 +1058,6 @@
                 0, true, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
     }
 
-    /*
-     * Install a package on internal flash via PackageManager install flag. Replace
-     * the package via flag to install on sdcard. Make sure the new flag overrides
-     * the old install location.
-     */
-    @LargeTest
-    public void testReplaceFlagInternalSdcard() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = 0;
-        int rFlags = PackageManager.INSTALL_EXTERNAL;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, rFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
-    /*
-     * Install a package on sdcard via PackageManager install flag. Replace
-     * the package with no flags or manifest option and make sure the old
-     * install location is retained.
-     */
-    @LargeTest
-    public void testReplaceFlagSdcardInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = 0;
-        InstallParams ip = sampleInstallFromRawResource(iFlags, false);
-        GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName);
-        int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING;
-        try {
-            invokeInstallPackage(ip.packageURI, replaceFlags, receiver, true);
-            assertInstall(ip.pkg, iFlags, ip.pkg.installLocation);
-        } catch (Exception e) {
-            failStr("Failed with exception : " + e);
-        } finally {
-            cleanUpInstall(ip);
-        }
-    }
-
     @LargeTest
     public void testManifestInstallLocationReplaceInternalSdcard() throws Exception {
         // Do not run on devices with emulated external storage.
@@ -1375,34 +1275,6 @@
     }
 
     @LargeTest
-    public void testMoveAppExternalToExternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_EXTERNAL_MEDIA;
-        boolean fail = true;
-        int result = PackageManager.MOVE_FAILED_INVALID_LOCATION;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
-    public void testMoveAppExternalToInternal() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int installFlags = PackageManager.INSTALL_EXTERNAL;
-        int moveFlags = PackageManager.MOVE_INTERNAL;
-        boolean fail = false;
-        int result = PackageManager.MOVE_SUCCEEDED;
-        sampleMoveFromRawResource(installFlags, moveFlags, fail, result);
-    }
-
-    @LargeTest
     public void testMoveAppFailInternalToExternalDelete() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1458,19 +1330,6 @@
     }
 
     /*
-     * Install an app on sdcard.
-     */
-    @LargeTest
-    public void testFlagE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, true);
-    }
-
-    /*
      * Install an app with both internal and manifest option set.
      * should install on internal.
      */
@@ -1506,59 +1365,6 @@
                 false, -1,
                 PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
-    /*
-     * Install an app with both external and manifest option set.
-     * should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * preferExternal. Should install externally.
-     */
-    @LargeTest
-    public void testFlagEManifestE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    /*
-     * Install an app with both external and manifest preference for
-     * auto. should install on external media.
-     */
-    @LargeTest
-    public void testFlagEManifestA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                PackageManager.INSTALL_EXTERNAL,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
 
     /*
      * The following test functions verify install location for existing apps.
@@ -1586,75 +1392,6 @@
                 -1);
     }
 
-    @LargeTest
-    public void testFlagIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_INTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_INTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
-    @LargeTest
-    public void testFlagEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_EXTERNAL | PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install,
-                rFlags,
-                true,
-                false, -1,
-                -1);
-    }
-
     /*
      * The following set of tests verify the installation of apps with
      * install location attribute set to internalOnly, preferExternal and auto.
@@ -1720,29 +1457,6 @@
     }
 
     @LargeTest
-    public void testManifestIExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_internal,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
-    }
-
-    @LargeTest
     public void testManifestEExistingI() throws Exception {
         // Do not run on devices with emulated external storage.
         if (Environment.isExternalStorageEmulated()) {
@@ -1766,29 +1480,6 @@
     }
 
     @LargeTest
-    public void testManifestEExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
     public void testManifestAExistingI() throws Exception {
         int iFlags = PackageManager.INSTALL_INTERNAL;
         int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
@@ -1806,29 +1497,6 @@
                 PackageInfo.INSTALL_LOCATION_AUTO);
     }
 
-    @LargeTest
-    public void testManifestAExistingE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        int rFlags = PackageManager.INSTALL_REPLACE_EXISTING;
-        // First install.
-        installFromRawResource("install.apk", R.raw.install,
-                iFlags,
-                false,
-                false, -1,
-                -1);
-        // Replace now
-        installFromRawResource("install.apk", R.raw.install_loc_auto,
-                rFlags,
-                true,
-                false, -1,
-                PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests check install location for existing
      * application based on user setting.
@@ -1896,42 +1564,6 @@
         setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
 
-    @LargeTest
-    public void testExistingEUserI() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_INTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserE() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_EXTERNAL;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
-    @LargeTest
-    public void testExistingEUserA() throws Exception {
-        // Do not run on devices with emulated external storage.
-        if (Environment.isExternalStorageEmulated()) {
-            return;
-        }
-
-        int userSetting = PackageHelper.APP_INSTALL_AUTO;
-        int iFlags = PackageManager.INSTALL_EXTERNAL;
-        setExistingXUserX(userSetting, iFlags, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
-    }
-
     /*
      * The following set of tests verify that the user setting defines
      * the install location.
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
new file mode 100644
index 0000000..dddeda3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceNestedService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+* Service used by {@link BinderWorkSourceTest}.
+*/
+public class BinderWorkSourceNestedService extends Service {
+    private final IBinderWorkSourceNestedService.Stub mBinder =
+            new IBinderWorkSourceNestedService.Stub() {
+
+        public int[] nestedCallWithWorkSourceToSet(int uidToBlame) {
+            final int uid =  Binder.getCallingWorkSourceUid();
+            if (uidToBlame != ThreadLocalWorkSource.UID_NONE) {
+                Binder.setCallingWorkSourceUid(uidToBlame);
+            }
+            final int nestedUid = callGetIncomingWorkSourceUid();
+            return new int[] {uid, nestedUid};
+        }
+
+        public int[] nestedCall() {
+            final int uid =  Binder.getCallingWorkSourceUid();
+            final int nestedUid = callGetIncomingWorkSourceUid();
+            return new int[] {uid, nestedUid};
+        }
+
+        private int callGetIncomingWorkSourceUid() {
+            BlockingQueue<IBinderWorkSourceService> blockingQueue =
+                    new LinkedBlockingQueue<>();
+            ServiceConnection mConnection = new ServiceConnection() {
+                public void onServiceConnected(ComponentName name, IBinder service) {
+                    blockingQueue.add(IBinderWorkSourceService.Stub.asInterface(service));
+                }
+
+                public void onServiceDisconnected(ComponentName name) {
+                }
+            };
+
+            Context context = getApplicationContext();
+            context.bindService(
+                    new Intent(context, BinderWorkSourceService.class),
+                    mConnection, Context.BIND_AUTO_CREATE);
+
+            final IBinderWorkSourceService service;
+            try {
+                service = blockingQueue.poll(30, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+            if (service == null) {
+                throw new RuntimeException("Gave up waiting for BinderWorkSourceService");
+            }
+
+            try {
+                return service.getIncomingWorkSourceUid();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            } finally {
+                context.unbindService(mConnection);
+            }
+        }
+    };
+
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceService.java b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
new file mode 100644
index 0000000..ac8d7ab
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceService.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import android.app.Service;
+import android.content.Intent;
+
+/**
+ * Service used by {@link BinderWorkSourceTest}.
+ */
+public class BinderWorkSourceService extends Service {
+    private final IBinderWorkSourceService.Stub mBinder =
+            new IBinderWorkSourceService.Stub() {
+        public int getIncomingWorkSourceUid() {
+            return Binder.getCallingWorkSourceUid();
+        }
+    };
+
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
new file mode 100644
index 0000000..ec17803
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test whether Binder calls work source is propagated correctly.
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BinderWorkSourceTest {
+    private static Context sContext;
+    private static final int UID = 100;
+    private static final int SECOND_UID = 200;
+    private static final int UID_NONE = ThreadLocalWorkSource.UID_NONE;
+
+    private IBinderWorkSourceService mService;
+    private IBinderWorkSourceNestedService mNestedService;
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = IBinderWorkSourceService.Stub.asInterface(service);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+    };
+
+    private ServiceConnection mNestedConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mNestedService = IBinderWorkSourceNestedService.Stub.asInterface(service);
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mNestedService = null;
+        }
+    };
+
+    @BeforeClass
+    public static void setUpOnce() throws Exception {
+        sContext = InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        sContext.bindService(
+                new Intent(sContext, BinderWorkSourceService.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+        sContext.bindService(
+                new Intent(sContext, BinderWorkSourceNestedService.class),
+                mNestedConnection, Context.BIND_AUTO_CREATE);
+
+        final long timeoutMs = System.currentTimeMillis() + 30_000;
+        while ((mService == null || mNestedService == null)
+                && System.currentTimeMillis() < timeoutMs) {
+            Thread.sleep(1_000);
+        }
+        assertNotNull("Gave up waiting for BinderWorkSourceService", mService);
+        assertNotNull("Gave up waiting for BinderWorkSourceNestedService", mNestedService);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        sContext.unbindService(mConnection);
+        sContext.unbindService(mNestedConnection);
+    }
+
+    @Test
+    public void setWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void clearWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        Binder.clearCallingWorkSource();
+        assertEquals(UID_NONE, mService.getIncomingWorkSourceUid());
+        assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void setWorkSource_propagatedForMultipleCalls() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void restoreWorkSource() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+        long token = Binder.clearCallingWorkSource();
+        Binder.restoreCallingWorkSource(token);
+
+        assertEquals(UID, mService.getIncomingWorkSourceUid());
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSoucePropagated() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+
+        int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(SECOND_UID);
+        assertEquals(UID, workSources[0]);
+        // UID set in ested call.
+        assertEquals(SECOND_UID, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSouceDoesNotEnablePropagation() throws Exception {
+        int[] workSources = mNestedService.nestedCallWithWorkSourceToSet(UID);
+        assertEquals(UID_NONE, workSources[0]);
+        // UID set in ested call.
+        assertEquals(UID, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID_NONE, Binder.getCallingWorkSourceUid());
+    }
+
+    @Test
+    public void nestedSetWorkSouceNotPropagated() throws Exception {
+        Binder.setCallingWorkSourceUid(UID);
+
+        int[] workSources = mNestedService.nestedCall();
+        assertEquals(UID, workSources[0]);
+        // No UID propagated.
+        assertEquals(UID_NONE, workSources[1]);
+        // Initial work source restored.
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
+    }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
index 27d25b8..365aebb 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceNestedService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -13,12 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package android.os;
+
+interface IBinderWorkSourceNestedService {
+    int[] nestedCallWithWorkSourceToSet(int uidToBlame);
+    int[] nestedCall();
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
index 27d25b8..05d4e82 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/tests/coretests/src/android/os/IBinderWorkSourceService.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2010 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.
@@ -13,12 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package android.os;
+
+interface IBinderWorkSourceService {
+    int getIncomingWorkSourceUid();
 }
diff --git a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
index c8bc35c..9e15231 100644
--- a/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
+++ b/core/tests/coretests/src/android/os/RedactingFileDescriptorTest.java
@@ -16,6 +16,10 @@
 
 package android.os;
 
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.RedactingFileDescriptor.removeRange;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
@@ -58,8 +62,8 @@
 
     @Test
     public void testSingleByte() throws Exception {
-        final FileDescriptor fd = RedactingFileDescriptor
-                .open(mContext, mFile, new long[] { 10, 11 }).getFileDescriptor();
+        final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+                new long[] { 10, 11 }).getFileDescriptor();
 
         final byte[] buf = new byte[1_000];
         assertEquals(buf.length, Os.read(fd, buf, 0, buf.length));
@@ -74,8 +78,8 @@
 
     @Test
     public void testRanges() throws Exception {
-        final FileDescriptor fd = RedactingFileDescriptor
-                .open(mContext, mFile, new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+        final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+                new long[] { 100, 200, 300, 400 }).getFileDescriptor();
 
         final byte[] buf = new byte[10];
         assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 90));
@@ -96,8 +100,8 @@
 
     @Test
     public void testEntireFile() throws Exception {
-        final FileDescriptor fd = RedactingFileDescriptor
-                .open(mContext, mFile, new long[] { 0, 5_000_000 }).getFileDescriptor();
+        final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_ONLY,
+                new long[] { 0, 5_000_000 }).getFileDescriptor();
 
         try (FileInputStream in = new FileInputStream(fd)) {
             int val;
@@ -106,4 +110,61 @@
             }
         }
     }
+
+    @Test
+    public void testReadWrite() throws Exception {
+        final FileDescriptor fd = RedactingFileDescriptor.open(mContext, mFile, MODE_READ_WRITE,
+                new long[] { 100, 200, 300, 400 }).getFileDescriptor();
+
+        // Redacted at first
+        final byte[] buf = new byte[10];
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+        assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 0, 0, 0 }, buf);
+
+        // But we can see data that we've written
+        Os.pwrite(fd, new byte[] { 32, 32 }, 0, 2, 102);
+        assertEquals(buf.length, Os.pread(fd, buf, 0, 10, 95));
+        assertArrayEquals(new byte[] { 64, 64, 64, 64, 64, 0, 0, 32, 32, 0 }, buf);
+    }
+
+    @Test
+    public void testRemoveRange() throws Exception {
+        // Removing outside ranges should have no changes
+        assertArrayEquals(new long[] { 100, 200, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 0, 100));
+        assertArrayEquals(new long[] { 100, 200, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 200, 300));
+        assertArrayEquals(new long[] { 100, 200, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 400, 500));
+
+        // Removing full regions
+        assertArrayEquals(new long[] { 100, 200 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 300, 400));
+        assertArrayEquals(new long[] { 100, 200 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 250, 450));
+        assertArrayEquals(new long[] { 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 50, 250));
+        assertArrayEquals(new long[] { },
+                removeRange(new long[] { 100, 200, 300, 400 }, 0, 5_000_000));
+    }
+
+    @Test
+    public void testRemoveRange_Partial() throws Exception {
+        assertArrayEquals(new long[] { 150, 200, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 50, 150));
+        assertArrayEquals(new long[] { 100, 150, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 150, 250));
+        assertArrayEquals(new long[] { 100, 150, 350, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 150, 350));
+        assertArrayEquals(new long[] { 100, 150 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 150, 500));
+    }
+
+    @Test
+    public void testRemoveRange_Hole() throws Exception {
+        assertArrayEquals(new long[] { 100, 125, 175, 200, 300, 400 },
+                removeRange(new long[] { 100, 200, 300, 400 }, 125, 175));
+        assertArrayEquals(new long[] { 100, 200 },
+                removeRange(new long[] { 100, 200 }, 150, 150));
+    }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 2c001c9..8a27de4b 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -126,6 +126,7 @@
                     Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
                     Settings.Global.AUTOFILL_SMART_SUGGESTION_EMULATION_FLAGS,
                     Settings.Global.AUTOMATIC_POWER_SAVER_MODE,
+                    Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED,
                     Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
                     Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
                     Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
@@ -192,6 +193,10 @@
                     Settings.Global.DATA_ROAMING,
                     Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS,
                     Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS,
+                    Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+                    Settings.Global.DATA_STALL_EVALUATION_TYPE,
+                    Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                    Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
                     Settings.Global.DEBUG_APP,
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
                     Settings.Global.DEFAULT_DNS_SERVER,
@@ -310,6 +315,7 @@
                     Settings.Global.MULTI_SIM_SMS_SUBSCRIPTION,
                     Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
                     Settings.Global.MULTI_SIM_VOICE_PROMPT,
+                    Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
                     Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
                     Settings.Global.NETSTATS_DEV_DELETE_AGE,
                     Settings.Global.NETSTATS_DEV_PERSIST_BYTES,
@@ -471,6 +477,7 @@
                     Settings.Global.USER_SWITCHER_ENABLED,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
+                    Settings.Global.USB_ALARM_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
                     Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED,
                     Settings.Global.WEBVIEW_MULTIPROCESS,
@@ -657,7 +664,10 @@
                  Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
                  Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
                  Settings.Secure.FLASHLIGHT_AVAILABLE,
-                 Settings.Secure.FLASHLIGHT_ENABLED);
+                 Settings.Secure.FLASHLIGHT_ENABLED,
+                 Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED,
+                 Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+                 Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
new file mode 100644
index 0000000..0c7e730
--- /dev/null
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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,d
+ * 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.text;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class AndroidCharacterTest {
+
+    @Test
+    public void testGetDirectionalities_nonSupplementaryCharacters() {
+        int size = Character.MAX_VALUE + 1
+                - (Character.MAX_SURROGATE - Character.MIN_SURROGATE + 1);
+        char[] chars = new char[size];
+        byte[] java_lang_results = new byte[size];
+        int index = 0;
+        for (int cp = 0; cp <= Character.MAX_VALUE; cp++) {
+            if (cp < Character.MIN_SURROGATE || cp > Character.MAX_SURROGATE) {
+                chars[index] = (char) cp;
+                java_lang_results[index] = Character.getDirectionality(cp);
+                index++;
+            }
+        }
+
+        byte[] android_text_results = new byte[size];
+        AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+        assertArrayEquals(java_lang_results, android_text_results);
+    }
+
+    @Test
+    public void testGetDirectionalities_supplementaryCharacters() {
+        int maxNumberOfChars = Character.MAX_CODE_POINT - Character.MIN_SUPPLEMENTARY_CODE_POINT
+                + 1;
+        int size = maxNumberOfChars * 2;
+        char[] chars = new char[size];
+        byte[] java_lang_results = new byte[size];
+        int index = 0;
+        for (int cp = Character.MIN_SUPPLEMENTARY_CODE_POINT; cp <= Character.MAX_CODE_POINT;
+                cp++) {
+            chars[index] = Character.highSurrogate(cp);
+            chars[index + 1] = Character.lowSurrogate(cp);
+            java_lang_results[index] = java_lang_results[index + 1] = Character
+                    .getDirectionality(cp);
+            index += 2;
+        }
+
+        byte[] android_text_results = new byte[size];
+        AndroidCharacter.getDirectionalities(chars, android_text_results, index);
+        assertArrayEquals(java_lang_results, android_text_results);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
new file mode 100644
index 0000000..ed80cd7
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsControllerTest {
+
+    private InsetsController mController = new InsetsController();
+
+    private SurfaceSession mSession = new SurfaceSession();
+    private SurfaceControl mLeash;
+
+    @Before
+    public void setup() {
+        mLeash = new SurfaceControl.Builder(mSession)
+                .setName("testSurface")
+                .build();
+    }
+
+    @Test
+    public void testControlsChanged() {
+        InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+        mController.onControlsChanged(new InsetsSourceControl[] { control });
+        assertEquals(mLeash,
+                mController.getSourceConsumer(TYPE_TOP_BAR).getControl().getLeash());
+    }
+
+    @Test
+    public void testControlsRevoked() {
+        InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash);
+        mController.onControlsChanged(new InsetsSourceControl[] { control });
+        mController.onControlsChanged(new InsetsSourceControl[0]);
+        assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
new file mode 100644
index 0000000..5a20ba2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl.Transaction;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceConsumerTest {
+
+    private InsetsSourceConsumer mConsumer;
+
+    private SurfaceSession mSession = new SurfaceSession();
+    private SurfaceControl mLeash;
+    @Mock Transaction mMockTransaction;
+    @Mock InsetsController mMockController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mLeash = new SurfaceControl.Builder(mSession)
+                .setName("testSurface")
+                .build();
+        mConsumer = new InsetsSourceConsumer(TYPE_TOP_BAR, new InsetsState(),
+                () -> mMockTransaction);
+        mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+    }
+
+    @Test
+    public void testHide() {
+        mConsumer.hide();
+        verify(mMockTransaction).hide(eq(mLeash));
+    }
+
+    @Test
+    public void testShow() {
+        mConsumer.hide();
+        mConsumer.show();
+        verify(mMockTransaction, atLeastOnce()).show(eq(mLeash));
+    }
+
+    @Test
+    public void testRestore() {
+        mConsumer.setControl(null);
+        reset(mMockTransaction);
+        mConsumer.hide();
+        verifyZeroInteractions(mMockTransaction);
+        mConsumer.setControl(new InsetsSourceControl(TYPE_TOP_BAR, mLeash));
+        verify(mMockTransaction).hide(eq(mLeash));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
new file mode 100644
index 0000000..ed472d2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsSourceTest {
+
+    private InsetsSource mSource = new InsetsSource(TYPE_NAVIGATION_BAR);
+
+    @Before
+    public void setUp() {
+        mSource.setVisible(true);
+    }
+
+    @Test
+    public void testCalculateInsetsTop() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsBottom() {
+        mSource.setFrame(new Rect(0, 400, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 100), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsLeft() {
+        mSource.setFrame(new Rect(0, 0, 100, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(100, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsetsRight() {
+        mSource.setFrame(new Rect(400, 0, 500, 500));
+        Insets insets = mSource.calculateInsets(new Rect(0, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 100, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_overextend() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_invisible() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                false /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 0, 0, 0), insets);
+    }
+
+    @Test
+    public void testCalculateInsets_ignoreVisibility() {
+        mSource.setFrame(new Rect(0, 0, 500, 100));
+        mSource.setVisible(false);
+        Insets insets = mSource.calculateInsets(new Rect(100, 0, 500, 500),
+                true /* ignoreVisibility */);
+        assertEquals(Insets.of(0, 100, 0, 0), insets);
+    }
+
+    // Parcel and equals already tested via InsetsStateTest
+}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
new file mode 100644
index 0000000..6bb9539
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.view;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@RunWith(AndroidJUnit4.class)
+public class InsetsStateTest {
+
+    private InsetsState mState = new InsetsState();
+    private InsetsState mState2 = new InsetsState();
+
+    @Test
+    public void testCalculateInsets() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_imeAndNav() {
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(100, insets.getStableInsetBottom());
+        assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testCalculateInsets_navRightStatusTop() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+        mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+    }
+
+    @Test
+    public void testStripForDispatch() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_TOP_BAR).setVisible(true);
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.removeSource(TYPE_IME);
+        WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+                DisplayCutout.NO_CUTOUT);
+        assertEquals(0, insets.getSystemWindowInsetBottom());
+    }
+
+    @Test
+    public void testEquals_differentRect() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_differentSource() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_sameButDifferentInsertOrder() {
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        assertEquals(mState, mState2);
+    }
+
+    @Test
+    public void testEquals_visibility() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        assertNotEquals(mState, mState2);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(TYPE_IME).setVisible(true);
+        mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+        Parcel p = Parcel.obtain();
+        mState.writeToParcel(p, 0 /* flags */);
+        mState2.readFromParcel(p);
+        p.recycle();
+        assertEquals(mState, mState2);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
new file mode 100644
index 0000000..bae2be3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/IntentFactoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.view.textclassifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.textclassifier.AnnotatorModel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentFactoryTest {
+
+    private static final String TEXT = "text";
+
+    @Test
+    public void create_typeDictionary() {
+        AnnotatorModel.ClassificationResult classificationResult =
+                new AnnotatorModel.ClassificationResult(
+                        TextClassifier.TYPE_DICTIONARY,
+                        1.0f,
+                        null,
+                        null);
+
+        List<TextClassifierImpl.LabeledIntent> intents = TextClassifierImpl.IntentFactory.create(
+                InstrumentationRegistry.getContext(),
+                TEXT,
+                false,
+                null,
+                classificationResult);
+
+        assertThat(intents).hasSize(1);
+        TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0);
+        Intent intent = labeledIntent.getIntent();
+        assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE);
+        assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 2ec35e9..fbcb629 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -23,6 +23,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.Intent;
 import android.os.LocaleList;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -64,6 +65,8 @@
     @Parameterized.Parameter
     public String mTextClassifierType;
 
+    private static final TextClassificationConstants TC_CONSTANTS =
+            TextClassificationConstants.loadFromString("");
     private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
@@ -80,7 +83,7 @@
     }
 
     @Test
-    public void testSmartSelection() {
+    public void testSuggestSelection() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Contact me at droid@android.com";
@@ -101,7 +104,7 @@
     }
 
     @Test
-    public void testSmartSelection_url() {
+    public void testSuggestSelection_url() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit http://www.android.com for more information";
@@ -157,7 +160,7 @@
     }
 
     @Test
-    public void testTextClassifyText_url() {
+    public void testClassifyText_url() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit www.android.com for more information";
@@ -174,7 +177,7 @@
     }
 
     @Test
-    public void testTextClassifyText_address() {
+    public void testClassifyText_address() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Brandschenkestrasse 110, Zürich, Switzerland";
@@ -188,7 +191,7 @@
     }
 
     @Test
-    public void testTextClassifyText_url_inCaps() {
+    public void testClassifyText_url_inCaps() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Visit HTTP://ANDROID.COM for more information";
@@ -205,7 +208,7 @@
     }
 
     @Test
-    public void testTextClassifyText_date() {
+    public void testClassifyText_date() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Let's meet on January 9, 2018.";
@@ -222,7 +225,7 @@
     }
 
     @Test
-    public void testTextClassifyText_datetime() {
+    public void testClassifyText_datetime() {
         if (isTextClassifierDisabled()) return;
 
         String text = "Let's meet 2018/01/01 10:30:20.";
@@ -240,6 +243,30 @@
     }
 
     @Test
+    public void testClassifyText_foreignText() {
+        LocaleList originalLocales = LocaleList.getDefault();
+        LocaleList.setDefault(LocaleList.forLanguageTags("en"));
+        String foreignText = "これは日本語のテキストです";
+
+        Context context = new FakeContextBuilder()
+                .setIntentComponent(Intent.ACTION_TRANSLATE, FakeContextBuilder.DEFAULT_COMPONENT)
+                .build();
+        TextClassifier classifier = new TextClassifierImpl(context, TC_CONSTANTS);
+        TextClassification.Request request = new TextClassification.Request.Builder(
+                foreignText, 0, foreignText.length())
+                .setDefaultLocales(LOCALES)
+                .build();
+
+        TextClassification classification = classifier.classifyText(request);
+        assertEquals(1, classification.getActions().size());
+        assertEquals(
+                context.getString(com.android.internal.R.string.translate),
+                classification.getActions().get(0).getTitle());
+
+        LocaleList.setDefault(originalLocales);
+    }
+
+    @Test
     public void testGenerateLinks_phone() {
         if (isTextClassifierDisabled()) return;
         String text = "The number is +12122537077. See you tonight!";
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 4456122..36792bb 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -467,6 +467,21 @@
         assertArrayEquals(container.mSharedViewNames, new String[] {"e0", "e1", "e2"});
     }
 
+    @Test
+    public void setIntTag() {
+        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
+        int index = 10;
+        views.setIntTag(
+                R.id.layout, com.android.internal.R.id.notification_action_index_tag, index);
+
+        RemoteViews recovered = parcelAndRecreate(views);
+        RemoteViews cloned = new RemoteViews(recovered);
+        View inflated = cloned.apply(mContext, mContainer);
+
+        assertEquals(
+                index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
+    }
+
     private class WidgetContainer extends AppWidgetHostView {
         int[] mSharedViewIds;
         String[] mSharedViewNames;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 9fcb06e..404c99c 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -103,6 +103,49 @@
     }
 
     @Test
+    public void setMaxHeight() throws Exception {
+        Intent sendIntent = createSendImageIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+        waitForIdle();
+
+        final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+        final View resolverList = activity.findViewById(R.id.resolver_list);
+        final int initialResolverHeight = resolverList.getHeight();
+
+        activity.runOnUiThread(() -> {
+            ResolverDrawerLayout layout = (ResolverDrawerLayout)
+                    activity.findViewById(
+                            R.id.contentPanel);
+            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+                = initialResolverHeight - 1;
+            // Force a relayout
+            layout.invalidate();
+            layout.requestLayout();
+        });
+        waitForIdle();
+        assertThat("Drawer should be capped at maxHeight",
+            resolverList.getHeight() == (initialResolverHeight - 1));
+
+        activity.runOnUiThread(() -> {
+            ResolverDrawerLayout layout = (ResolverDrawerLayout)
+                    activity.findViewById(
+                            R.id.contentPanel);
+            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+                = initialResolverHeight + 1;
+            // Force a relayout
+            layout.invalidate();
+            layout.requestLayout();
+        });
+        waitForIdle();
+        assertThat("Drawer should not change height if its height is less than maxHeight",
+            resolverList.getHeight() == initialResolverHeight);
+    }
+
+    @Test
     public void setShowAtTopToTrue() throws Exception {
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 30309cf..02a76f8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.os;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
@@ -387,8 +388,7 @@
 
     @Test
     public void testNoDataCollectedBeforeInitialDeviceStateSet() {
-        TestBinderCallsStats bcs = new TestBinderCallsStats();
-        bcs.setDeviceState(null);
+        TestBinderCallsStats bcs = new TestBinderCallsStats(null);
         bcs.setDetailedTracking(true);
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
@@ -557,6 +557,83 @@
         assertEquals(0, bcs.getExceptionCounts().size());
     }
 
+    @Test
+    public void testOverflow_sameEntry() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.setSamplingInterval(1);
+        bcs.setMaxBinderCallStats(2);
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        BinderCallsStats.UidEntry uidEntry = bcs.getUidEntries().get(WORKSOURCE_UID);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(1, callStatsList.size());
+        BinderCallsStats.CallStat callStats = callStatsList.get(0);
+        assertEquals(3, callStats.callCount);
+    }
+
+    @Test
+    public void testOverflow_overflowEntry() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.setSamplingInterval(1);
+        bcs.setMaxBinderCallStats(1);
+
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        callSession = bcs.callStarted(binder, 2);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.ExportedCallStat> callStatsList = bcs.getExportedCallStats();
+        assertEquals(2, callStatsList.size());
+        BinderCallsStats.ExportedCallStat callStats = callStatsList.get(0);
+        assertEquals(1, callStats.callCount);
+        assertEquals("1", callStats.methodName);
+        assertEquals("android.os.Binder", callStats.className);
+        assertEquals(CALLING_UID, callStats.callingUid);
+
+        callStats = callStatsList.get(1);
+        assertEquals(1, callStats.callCount);
+        assertEquals("-1", callStats.methodName);
+        assertEquals("com.android.internal.os.BinderCallsStats$OverflowBinder",
+                callStats.className);
+        assertEquals(CALLING_UID, callStats.callingUid);
+    }
+
+    @Test
+    public void testAddsDebugEntries() {
+        long startTime = System.currentTimeMillis();
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setAddDebugEntries(true);
+        ArrayList<BinderCallsStats.ExportedCallStat> callStats = bcs.getExportedCallStats();
+        assertEquals(3, callStats.size());
+        BinderCallsStats.ExportedCallStat debugEntry1 = callStats.get(0);
+        assertEquals("", debugEntry1.className);
+        assertEquals("__DEBUG_start_time_millis", debugEntry1.methodName);
+        assertTrue(startTime <= debugEntry1.maxReplySizeBytes);
+        BinderCallsStats.ExportedCallStat debugEntry2 = callStats.get(1);
+        assertEquals("", debugEntry2.className);
+        assertEquals("__DEBUG_end_time_millis", debugEntry2.methodName);
+        assertTrue(debugEntry1.maxReplySizeBytes <= debugEntry2.maxReplySizeBytes);
+        BinderCallsStats.ExportedCallStat debugEntry3 = callStats.get(2);
+        assertEquals("", debugEntry3.className);
+        assertEquals("__DEBUG_battery_time_millis", debugEntry3.methodName);
+        assertTrue(debugEntry3.maxReplySizeBytes >= 0);
+    }
+
     class TestBinderCallsStats extends BinderCallsStats {
         public int callingUid = CALLING_UID;
         public int workSourceUid = WORKSOURCE_UID;
@@ -564,6 +641,10 @@
         public long elapsedTime = 0;
 
         TestBinderCallsStats() {
+            this(mDeviceState);
+        }
+
+        TestBinderCallsStats(CachedDeviceState deviceState) {
             // Make random generator not random.
             super(new Injector() {
                 public Random getRandomGenerator() {
@@ -577,7 +658,10 @@
                 }
             });
             setSamplingInterval(1);
-            setDeviceState(mDeviceState.getReadonlyClient());
+            setAddDebugEntries(false);
+            if (deviceState != null) {
+                setDeviceState(deviceState.getReadonlyClient());
+            }
         }
 
         @Override
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index 385bad5..b242a34 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -39,9 +39,7 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.List;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -58,8 +56,8 @@
             1000, 2000, 3000, 4000,
     };
     private static final int[][] THREAD_CPU_TIMES = {
-            {100, 0, 0, 100},
-            {0, 0, 9999999, 0},
+            {1, 0, 0, 1},
+            {0, 0, 0, 0},
             {1000, 1000, 1000, 1000},
             {0, 1, 2, 3},
     };
@@ -110,42 +108,6 @@
     }
 
     @Test
-    public void testReader_filtersLowTotalCpuUsage() throws IOException {
-        KernelCpuThreadReader.Injector processUtils =
-                new KernelCpuThreadReader.Injector() {
-                    @Override
-                    public int myPid() {
-                        return PROCESS_ID;
-                    }
-
-                    @Override
-                    public int myUid() {
-                        return UID;
-                    }
-
-                    @Override
-                    public int getUidForPid(int pid) {
-                        return 0;
-                    }
-                };
-        setupDirectory(mProcDirectory.toPath().resolve("self"), new int[]{1, 2}, PROCESS_NAME,
-                THREAD_NAMES, new int[]{1000, 2000}, new int[][]{{0, 1}, {100, 0}});
-
-        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
-                mProcDirectory.toPath(),
-                mProcDirectory.toPath().resolve("self/task/1/time_in_state"),
-                processUtils);
-        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                kernelCpuThreadReader.getCurrentProcessCpuUsage();
-
-        List<Integer> threadIds = processCpuUsage.threadCpuUsages.stream()
-                .map(t -> t.threadId)
-                .collect(Collectors.toList());
-        assertEquals(1, threadIds.size());
-        assertEquals(2, (long) threadIds.get(0));
-    }
-
-    @Test
     public void testReader_byUids() throws IOException {
         int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
         Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -172,7 +134,7 @@
             setupDirectory(mProcDirectory.toPath().resolve(String.valueOf(uid)),
                     new int[]{uid * 10},
                     "process" + uid, new String[]{"thread" + uid}, new int[]{1000},
-                    new int[][]{{uid + 100}});
+                    new int[][]{{uid}});
         }
         final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
                 mProcDirectory.toPath(),
@@ -189,7 +151,7 @@
             int uid = expectedUids[i];
             checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(),
                     uid, uid, new int[]{uid * 10}, "process" + uid, new String[]{"thread" + uid},
-                    new int[]{1000}, new int[][]{{uid + 100}});
+                    new int[]{1000}, new int[][]{{uid}});
         }
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
index 31dde5c..3d7801c 100644
--- a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -273,8 +273,7 @@
 
     @Test
     public void testDataNotCollectedBeforeDeviceStateSet() {
-        TestableLooperStats looperStats = new TestableLooperStats(1, 100);
-        looperStats.setDeviceState(null);
+        TestableLooperStats looperStats = new TestableLooperStats(1, 100, null);
 
         Object token1 = looperStats.messageDispatchStarting();
         looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
@@ -439,7 +438,7 @@
         looperStats.messageDispatched(token, message);
 
         List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
-        assertThat(entries).hasSize(3);
+        assertThat(entries).hasSize(4);
         LooperStats.ExportedEntry debugEntry1 = entries.get(1);
         assertThat(debugEntry1.handlerClassName).isEqualTo("");
         assertThat(debugEntry1.messageName).isEqualTo("__DEBUG_start_time_millis");
@@ -448,6 +447,10 @@
         assertThat(debugEntry2.handlerClassName).isEqualTo("");
         assertThat(debugEntry2.messageName).isEqualTo("__DEBUG_end_time_millis");
         assertThat(debugEntry2.maxDelayMillis).isAtLeast(looperStats.getStartTimeMillis());
+        LooperStats.ExportedEntry debugEntry3 = entries.get(3);
+        assertThat(debugEntry3.handlerClassName).isEqualTo("");
+        assertThat(debugEntry3.messageName).isEqualTo("__DEBUG_battery_time_millis");
+        assertThat(debugEntry3.maxDelayMillis).isAtLeast(0L);
     }
 
     private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
@@ -468,10 +471,16 @@
         private int mSamplingInterval;
 
         TestableLooperStats(int samplingInterval, int sizeCap) {
+            this(samplingInterval, sizeCap, mDeviceState);
+        }
+
+        TestableLooperStats(int samplingInterval, int sizeCap, CachedDeviceState deviceState) {
             super(samplingInterval, sizeCap);
-            this.mSamplingInterval = samplingInterval;
-            this.setDeviceState(mDeviceState.getReadonlyClient());
-            this.setAddDebugEntries(false);
+            mSamplingInterval = samplingInterval;
+            setAddDebugEntries(false);
+            if (deviceState != null) {
+                setDeviceState(deviceState.getReadonlyClient());
+            }
         }
 
         void tickRealtime(long micros) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3b0dc9d..135c137 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -64,7 +64,7 @@
     public boolean isRecordingFor(Object o) { return false; }
 
     // may be null
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 117521088)
     private Bitmap mBitmap;
 
     // optional field set by the caller
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2e1d81a..2227cf5 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1779,6 +1779,70 @@
     }
 
     /**
+     * <p>Computes the chromaticity coordinates of a CIE series D illuminant
+     * from the specified correlated color temperature (CCT). The specified CCT
+     * must be greater than 0. A meaningful CCT range is [4000, 25000].</p>
+     *
+     * <p>The transform is computed using the methods referred to in Kang et
+     * al., <i>Design of Advanced Color - Temperature Control System for HDTV
+     * Applications</i>, Journal of Korean Physical Society 41, 865-871
+     * (2002).</p>
+     *
+     * @param cct The correlated color temperature, in Kelvin
+     * @return Corresponding XYZ values
+     * @throws IllegalArgumentException If cct is invalid
+     */
+    @NonNull
+    @Size(3)
+    public static float[] cctToIlluminantdXyz(@IntRange(from = 1) int cct) {
+        if (cct < 1) {
+            throw new IllegalArgumentException("Temperature must be greater than 0");
+        }
+
+        final float icct = 1.0f / cct;
+        final float icct2 = icct * icct;
+        final float x = cct <= 7000.0f ?
+            0.244063f + 0.09911e3f * icct + 2.9678e6f * icct2 - 4.6070e9f * icct2 * icct :
+            0.237040f + 0.24748e3f * icct + 1.9018e6f * icct2 - 2.0064e9f * icct2 * icct;
+        final float y = -3.0f * x * x + 2.87f * x - 0.275f;
+        return xyYToXyz(new float[] {x, y});
+    }
+
+    /**
+     * <p>Computes the chromatic adaptation transform from the specified
+     * source white point to the specified destination white point.</p>
+     *
+     * <p>The transform is computed using the von Kries method, described
+     * in more details in the documentation of {@link Adaptation}. The
+     * {@link Adaptation} enum provides different matrices that can be
+     * used to perform the adaptation.</p>
+     *
+     * @param adaptation The adaptation method
+     * @param srcWhitePoint The white point to adapt from
+     * @param dstWhitePoint The white point to adapt to
+     * @return A 3x3 matrix as a non-null array of 9 floats
+     */
+    @NonNull
+    @Size(9)
+    public static float[] chromaticAdaptation(@NonNull Adaptation adaptation,
+            @NonNull @Size(min = 2, max = 3) float[] srcWhitePoint,
+            @NonNull @Size(min = 2, max = 3) float[] dstWhitePoint) {
+        float[] srcXyz = srcWhitePoint.length == 3 ?
+            Arrays.copyOf(srcWhitePoint, 3) : xyYToXyz(srcWhitePoint);
+        float[] dstXyz = dstWhitePoint.length == 3 ?
+            Arrays.copyOf(dstWhitePoint, 3) : xyYToXyz(dstWhitePoint);
+
+        if (compare(srcXyz, dstXyz)) {
+            return new float[] {
+                1.0f, 0.0f, 0.0f,
+                0.0f, 1.0f, 0.0f,
+                0.0f, 0.0f, 1.0f
+            };
+        }
+        return chromaticAdaptation(adaptation.mTransform, srcXyz, dstXyz);
+    }
+
+    /**
      * Implementation of the CIE XYZ color space. Assumes the white point is D50.
      */
     @AnyThread
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index de110c8..d9da27c 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -82,6 +82,17 @@
     }
 
     /**
+     * Add two Insets.
+     *
+     * @param a The first Insets to add.
+     * @param b The second Insets to add.
+     * @return a + b, i. e. all insets on every side are added together.
+     */
+    public static @NonNull Insets add(@NonNull Insets a, @NonNull Insets b) {
+        return Insets.of(a.left + b.left, a.top + b.top, a.right + b.right, a.bottom + b.bottom);
+    }
+
+    /**
      * Two Insets instances are equal iff they belong to the same class and their fields are
      * pairwise equal.
      *
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index c4dc0ad..40a32f3 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -106,6 +106,20 @@
     }
 
     /**
+     * @hide
+     */
+    public Rect(@Nullable Insets r) {
+        if (r == null) {
+            left = top = right = bottom = 0;
+        } else {
+            left = r.left;
+            top = r.top;
+            right = r.right;
+            bottom = r.bottom;
+        }
+    }
+
+    /**
      * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
      *
      * @hide
@@ -418,6 +432,18 @@
     }
 
     /**
+     * Insets the rectangle on all sides specified by the dimensions of {@code insets}.
+     * @hide
+     * @param insets The insets to inset the rect by.
+     */
+    public void inset(Insets insets) {
+        left += insets.left;
+        top += insets.top;
+        right -= insets.right;
+        bottom -= insets.bottom;
+    }
+
+    /**
      * Insets the rectangle on all sides specified by the insets.
      * @hide
      * @param left The amount to add from the rectangle's left
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 45d7a21..d6f08b9 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -1173,6 +1173,22 @@
         return nGetAllowForceDark(mNativeRenderNode);
     }
 
+    /**
+     * Returns the unique ID that identifies this RenderNode. This ID is unique for the
+     * lifetime of the process. IDs are reset on process death, and are unique only within
+     * the process.
+     *
+     * This ID is intended to be used with debugging tools to associate a particular
+     * RenderNode across different debug dumping & inspection tools. For example
+     * a View layout inspector should include the unique ID for any RenderNodes that it owns
+     * to associate the drawing content with the layout content.
+     *
+     * @return the unique ID for this RenderNode
+     */
+    public long getUniqueId() {
+        return nGetUniqueId(mNativeRenderNode);
+    }
+
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
@@ -1479,4 +1495,7 @@
 
     @CriticalNative
     private static native boolean nGetAllowForceDark(long renderNode);
+
+    @CriticalNative
+    private static native long nGetUniqueId(long renderNode);
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index bf969ef..9b86b77 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -163,9 +163,6 @@
     private int[] mSupportedAxes;
     private static final int[] EMPTY_AXES = {};
 
-    // The underlying font families.
-    private final FontFamily[] mFamilies;
-
     @UnsupportedAppUsage
     private static void setDefault(Typeface t) {
         sDefaultTypeface = t;
@@ -252,7 +249,22 @@
             if (familyBuilder == null) {
                 return Typeface.DEFAULT;
             }
-            typeface = new Typeface.CustomFallbackBuilder(familyBuilder.build()).build();
+            final FontFamily family = familyBuilder.build();
+            final FontStyle normal = new FontStyle(FontStyle.FONT_WEIGHT_NORMAL,
+                    FontStyle.FONT_SLANT_UPRIGHT);
+            Font bestFont = family.getFont(0);
+            int bestScore = normal.getMatchScore(bestFont.getStyle());
+            for (int i = 1; i < family.getSize(); ++i) {
+                final Font candidate = family.getFont(i);
+                final int score = normal.getMatchScore(candidate.getStyle());
+                if (score < bestScore) {
+                    bestFont = candidate;
+                    bestScore = score;
+                }
+            }
+            typeface = new Typeface.CustomFallbackBuilder(family)
+                    .setStyle(bestFont.getStyle())
+                    .build();
         } catch (IOException e) {
             typeface = Typeface.DEFAULT;
         }
@@ -652,6 +664,18 @@
         private @Nullable FontStyle mStyle;
 
         /**
+         * Returns the maximum capacity of custom fallback families.
+         *
+         * This includes the the first font family passed to the constructor.
+         * It is guaranteed that the value will be greater than or equal to 64.
+         *
+         * @return the maximum number of font families for the custom fallback
+         */
+        public static @IntRange(from = 64) int getMaxCustomFallbackCount() {
+            return MAX_CUSTOM_FALLBACK;
+        }
+
+        /**
          * Constructs a builder with a font family.
          *
          * @param family a family object
@@ -706,8 +730,8 @@
          */
         public CustomFallbackBuilder addCustomFallback(@NonNull FontFamily family) {
             Preconditions.checkNotNull(family);
-            Preconditions.checkArgument(mFamilies.size() < MAX_CUSTOM_FALLBACK,
-                    "Custom fallback limit exceeded(" + MAX_CUSTOM_FALLBACK + ")");
+            Preconditions.checkArgument(mFamilies.size() < getMaxCustomFallbackCount(),
+                    "Custom fallback limit exceeded(" + getMaxCustomFallbackCount() + ")");
             mFamilies.add(family);
             return this;
         }
@@ -720,21 +744,17 @@
         public Typeface build() {
             final int userFallbackSize = mFamilies.size();
             final FontFamily[] fallback = SystemFonts.getSystemFallback(mFallbackName);
-            final FontFamily[] fullFamilies = new FontFamily[fallback.length + userFallbackSize];
             final long[] ptrArray = new long[fallback.length + userFallbackSize];
             for (int i = 0; i < userFallbackSize; ++i) {
                 ptrArray[i] = mFamilies.get(i).getNativePtr();
-                fullFamilies[i] = mFamilies.get(i);
             }
             for (int i = 0; i < fallback.length; ++i) {
                 ptrArray[i + userFallbackSize] = fallback[i].getNativePtr();
-                fullFamilies[i + userFallbackSize] = fallback[i];
             }
             final int weight = mStyle == null ? 400 : mStyle.getWeight();
             final int italic =
                     (mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ?  0 : 1;
-
-            return new Typeface(nativeCreateFromArray(ptrArray, weight, italic), fullFamilies);
+            return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
         }
     }
 
@@ -799,7 +819,7 @@
                 }
             }
 
-            typeface = new Typeface(nativeCreateFromTypeface(ni, style), family.mFamilies);
+            typeface = new Typeface(nativeCreateFromTypeface(ni, style));
             styles.put(style, typeface);
         }
         return typeface;
@@ -867,8 +887,7 @@
             }
 
             typeface = new Typeface(
-                    nativeCreateFromTypefaceWithExactStyle(
-                            base.native_instance, weight, italic), base.mFamilies);
+                    nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic));
             innerCache.put(key, typeface);
         }
         return typeface;
@@ -878,8 +897,7 @@
     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
             @NonNull List<FontVariationAxis> axes) {
         final Typeface base = family == null ? Typeface.DEFAULT : family;
-        return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
-                base.mFamilies);
+        return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes));
     }
 
     /**
@@ -985,7 +1003,7 @@
             ptrArray[i] = families[i].getNativePtr();
         }
         return new Typeface(nativeCreateFromArray(ptrArray,
-                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), families);
+                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     }
 
     /**
@@ -1033,19 +1051,6 @@
         }
 
         native_instance = ni;
-        mFamilies = new FontFamily[0];
-        sRegistry.registerNativeAllocation(this, native_instance);
-        mStyle = nativeGetStyle(ni);
-        mWeight = nativeGetWeight(ni);
-    }
-
-    private Typeface(long ni, @NonNull FontFamily[] families) {
-        if (ni == 0) {
-            throw new IllegalStateException("native typeface cannot be made");
-        }
-
-        native_instance = ni;
-        mFamilies = families;
         sRegistry.registerNativeAllocation(this, native_instance);
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
@@ -1072,8 +1077,7 @@
             final Typeface base = systemFontMap.get(alias.getToName());
             final int weight = alias.getWeight();
             final Typeface newFace = weight == 400 ? base :
-                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight),
-                            base.mFamilies);
+                    new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
             systemFontMap.put(alias.getName(), newFace);
         }
     }
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index dea2f45..cb12a7c 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -150,11 +151,14 @@
      */
     AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
         mLayerState = createConstantState(state, res);
-
-        if (sMask == null) {
-            sMask = PathParser.createPathFromPathData(
-                Resources.getSystem().getString(R.string.config_icon_mask));
-        }
+        // config_icon_mask from context bound resource may have been chaged using
+        // OverlayManager. Read that one first.
+        Resources r = ActivityThread.currentActivityThread() == null
+                ? Resources.getSystem()
+                : ActivityThread.currentActivityThread().getApplication().getResources();
+        // TODO: either make sMask update only when config_icon_mask changes OR
+        // get rid of it all-together in layoutlib
+        sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
         mMask = new Path(sMask);
         mMaskScaleOnly = new Path(mMask);
         mMaskMatrix = new Matrix();
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index caf610b..5bd59d4 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -713,11 +713,12 @@
     }
 
     /**
-     * Whether this drawable requests projection.
+     * Whether this drawable requests projection. Indicates that the
+     * {@link android.graphics.RenderNode} this Drawable will draw into should be drawn immediately
+     * after the closest ancestor RenderNode containing a projection receiver.
      *
-     * @hide magic!
+     * @see android.graphics.RenderNode#setProjectBackwards(boolean)
      */
-    @UnsupportedAppUsage
     public boolean isProjected() {
         return false;
     }
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 8740234..991847a 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1571,15 +1571,32 @@
         st.mGradient = a.getInt(
                 R.styleable.GradientDrawableGradient_type, st.mGradient);
 
-        // TODO: Update these to be themeable.
+        final boolean hasGradientColors = st.mGradientColors != null;
+        final boolean hasGradientCenter = st.hasCenterColor();
+        final int prevStart = hasGradientColors ? st.mGradientColors[0] : 0;
+        final int prevCenter = hasGradientCenter ? st.mGradientColors[1] : 0;
+        final int prevEnd;
+
+        if (st.hasCenterColor()) {
+            // if there is a center color, the end color is the last of the 3 values
+            prevEnd = st.mGradientColors[2];
+        } else if (hasGradientColors) {
+            // if there is not a center color but there are already colors configured, then
+            // the end color is the 2nd value in the array
+            prevEnd = st.mGradientColors[1];
+        } else {
+            // otherwise, there isn't a previously configured end color
+            prevEnd = 0;
+        }
+
         final int startColor = a.getColor(
-                R.styleable.GradientDrawableGradient_startColor, 0);
+                R.styleable.GradientDrawableGradient_startColor, prevStart);
         final boolean hasCenterColor = a.hasValue(
-                R.styleable.GradientDrawableGradient_centerColor);
+                R.styleable.GradientDrawableGradient_centerColor) || hasGradientCenter;
         final int centerColor = a.getColor(
-                R.styleable.GradientDrawableGradient_centerColor, 0);
+                R.styleable.GradientDrawableGradient_centerColor, prevCenter);
         final int endColor = a.getColor(
-                R.styleable.GradientDrawableGradient_endColor, 0);
+                R.styleable.GradientDrawableGradient_endColor, prevEnd);
 
         if (hasCenterColor) {
             st.mGradientColors = new int[3];
@@ -1943,6 +1960,10 @@
             }
         }
 
+        public boolean hasCenterColor() {
+            return mGradientColors != null && mGradientColors.length == 3;
+        }
+
         private void applyDensityScaling(int sourceDensity, int targetDensity) {
             if (mInnerRadius > 0) {
                 mInnerRadius = Drawable.scaleFromDensity(
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index 82fc7ac..af517d6 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 
 import com.android.internal.util.Preconditions;
@@ -232,6 +233,16 @@
         return mSlant;
     }
 
+    /**
+     * Compute the matching score for another style.
+     *
+     * The smaller is better.
+     * @hide
+     */
+    public int getMatchScore(@NonNull FontStyle o) {
+        return Math.abs((getWeight() - o.getWeight())) / 100 + (getSlant() == o.getSlant() ? 0 : 2);
+    }
+
     @Override
     public boolean equals(@Nullable Object o) {
         if (o == this) {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7ab12b1..ad9ec02 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -217,10 +217,19 @@
   ATRACE_NAME("AssetManager::GetResourceConfigurations");
   std::set<ResTable_config> configurations;
   for (const PackageGroup& package_group : package_groups_) {
+    bool found_system_package = false;
     for (const ConfiguredPackage& package : package_group.packages_) {
       if (exclude_system && package.loaded_package_->IsSystem()) {
+        found_system_package = true;
         continue;
       }
+
+      if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+        // Overlays must appear after the target package to take effect. Any overlay found in the
+        // same package as a system package is able to overlay system resources.
+        continue;
+      }
+
       package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
     }
   }
@@ -232,10 +241,19 @@
   ATRACE_NAME("AssetManager::GetResourceLocales");
   std::set<std::string> locales;
   for (const PackageGroup& package_group : package_groups_) {
+    bool found_system_package = false;
     for (const ConfiguredPackage& package : package_group.packages_) {
       if (exclude_system && package.loaded_package_->IsSystem()) {
+        found_system_package = true;
         continue;
       }
+
+      if (exclude_system && package.loaded_package_->IsOverlay() && found_system_package) {
+        // Overlays must appear after the target package to take effect. Any overlay found in the
+        // same package as a system package is able to overlay system resources.
+        continue;
+      }
+
       package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
     }
   }
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 68d216d..c20c720 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -583,7 +583,65 @@
           loaded_package->dynamic_package_map_.emplace_back(std::move(package_name),
                                                             dtohl(entry_iter->packageId));
         }
+      } break;
 
+      case RES_TABLE_OVERLAYABLE_TYPE: {
+        const ResTable_overlayable_header* header =
+            child_chunk.header<ResTable_overlayable_header>();
+        if (header == nullptr) {
+          LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small.";
+          return {};
+        }
+
+        // Iterate over the overlayable policy chunks
+        ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
+        while (overlayable_iter.HasNext()) {
+          const Chunk overlayable_child_chunk = overlayable_iter.Next();
+
+          switch (overlayable_child_chunk.type()) {
+            case RES_TABLE_OVERLAYABLE_POLICY_TYPE: {
+              const ResTable_overlayable_policy_header* policy_header =
+                  overlayable_child_chunk.header<ResTable_overlayable_policy_header>();
+              if (policy_header == nullptr) {
+                LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
+                return {};
+              }
+
+              if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
+                  < dtohl(policy_header->entry_count)) {
+                LOG(ERROR) <<  "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
+                return {};
+              }
+
+              // Retrieve all the ids belonging to this policy
+              std::unordered_set<uint32_t> ids;
+              const auto ids_begin =
+                  reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr());
+              const auto ids_end = ids_begin + dtohl(policy_header->entry_count);
+              for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) {
+                ids.insert(dtohl(id_iter->ident));
+              }
+
+              // Add the pairing of overlayable properties to resource ids to the package
+              OverlayableInfo overlayable_info;
+              overlayable_info.policy_flags = policy_header->policy_flags;
+              loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids));
+              break;
+            }
+
+            default:
+              LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
+              break;
+          }
+        }
+
+        if (overlayable_iter.HadError()) {
+          LOG(ERROR) << StringPrintf("Error parsing RES_TABLE_OVERLAYABLE_POLICY_TYPE: %s",
+                                     overlayable_iter.GetLastError().c_str());
+          if (overlayable_iter.HadFatalError()) {
+            return {};
+          }
+        }
       } break;
 
       default:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f7fb89e..63b2527 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7035,40 +7035,70 @@
     return NO_ERROR;
 }
 
-struct IdmapMatchingResources {
-    void Add(uint32_t targetResId, uint32_t overlayResId) {
-        uint8_t targetTypeid = Res_GETTYPE(targetResId);
-        if (typeMappings.find(targetTypeid) == typeMappings.end()) {
-            typeMappings.emplace(targetTypeid, std::set<std::pair<uint32_t, uint32_t>>());
+class IdmapMatchingResources;
+
+class IdmapTypeMapping {
+public:
+    void add(uint32_t targetResId, uint32_t overlayResId) {
+        uint8_t targetTypeId = Res_GETTYPE(targetResId);
+        if (mData.find(targetTypeId) == mData.end()) {
+            mData.emplace(targetTypeId, std::set<std::pair<uint32_t, uint32_t>>());
         }
-        auto& entries = typeMappings[targetTypeid];
+        auto& entries = mData[targetTypeId];
         entries.insert(std::make_pair(targetResId, overlayResId));
     }
 
-    void FixPadding() {
-        for (auto ti = typeMappings.cbegin(); ti != typeMappings.cend(); ++ti) {
-            uint32_t last_seen = 0xffffffff;
-            size_t total_entries = 0;
+    bool empty() const {
+        return mData.empty();
+    }
+
+private:
+    // resource type ID in context of target -> set of resource entries mapping target -> overlay
+    std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> mData;
+
+    friend IdmapMatchingResources;
+};
+
+class IdmapMatchingResources {
+public:
+    IdmapMatchingResources(std::unique_ptr<IdmapTypeMapping> tm) : mTypeMapping(std::move(tm)) {
+        assert(mTypeMapping);
+        for (auto ti = mTypeMapping->mData.cbegin(); ti != mTypeMapping->mData.cend(); ++ti) {
+            uint32_t lastSeen = 0xffffffff;
+            size_t totalEntries = 0;
             for (auto ei = ti->second.cbegin(); ei != ti->second.cend(); ++ei) {
-                assert(last_seen == 0xffffffff || last_seen < ei->first);
-                entryPadding[ei->first] = (last_seen == 0xffffffff) ? 0 : ei->first - last_seen - 1;
-                last_seen = ei->first;
-                total_entries += 1 + entryPadding[ei->first];
+                assert(lastSeen == 0xffffffff || lastSeen < ei->first);
+                mEntryPadding[ei->first] = (lastSeen == 0xffffffff) ? 0 : ei->first - lastSeen - 1;
+                lastSeen = ei->first;
+                totalEntries += 1 + mEntryPadding[ei->first];
             }
-            numberOfEntriesIncludingPadding[ti->first] = total_entries;
+            mNumberOfEntriesIncludingPadding[ti->first] = totalEntries;
         }
     }
 
+    const std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>>& getTypeMapping() const {
+        return mTypeMapping->mData;
+    }
+
+    size_t getNumberOfEntriesIncludingPadding(uint8_t type) const {
+        return mNumberOfEntriesIncludingPadding.at(type);
+    }
+
+    size_t getPadding(uint32_t resid) const {
+        return mEntryPadding.at(resid);
+    }
+
+private:
     // resource type ID in context of target -> set of resource entries mapping target -> overlay
-    std::map<uint8_t, std::set<std::pair<uint32_t, uint32_t>>> typeMappings;
+    const std::unique_ptr<IdmapTypeMapping> mTypeMapping;
 
     // resource ID in context of target -> trailing padding for that resource (call FixPadding
     // before use)
-    std::map<uint32_t, size_t> entryPadding;
+    std::map<uint32_t, size_t> mEntryPadding;
 
     // resource type ID in context of target -> total number of entries, including padding entries,
     // for that type (call FixPadding before use)
-    std::map<uint8_t, size_t> numberOfEntriesIncludingPadding;
+    std::map<uint8_t, size_t> mNumberOfEntriesIncludingPadding;
 };
 
 status_t ResTable::createIdmap(const ResTable& targetResTable,
@@ -7098,7 +7128,8 @@
         return UNKNOWN_ERROR;
     }
 
-    const ResTable_package* targetPackageStruct = targetResTable.mPackageGroups[0]->packages[0]->package;
+    const ResTable_package* targetPackageStruct =
+        targetResTable.mPackageGroups[0]->packages[0]->package;
     const size_t tmpNameSize = arraysize(targetPackageStruct->name);
     char16_t tmpName[tmpNameSize];
     strcpy16_dtoh(tmpName, targetPackageStruct->name, tmpNameSize);
@@ -7106,11 +7137,8 @@
 
     const PackageGroup* packageGroup = mPackageGroups[0];
 
-    // the number of resources overlaid that were not explicitly marked overlayable
-    size_t forcedOverlayCount = 0u;
-
     // find the resources that exist in both packages
-    IdmapMatchingResources matchingResources;
+    auto typeMapping = std::make_unique<IdmapTypeMapping>();
     for (size_t typeIndex = 0; typeIndex < packageGroup->types.size(); ++typeIndex) {
         const TypeList& typeList = packageGroup->types[typeIndex];
         if (typeList.isEmpty()) {
@@ -7139,29 +7167,25 @@
                 continue;
             }
 
-            if ((dtohl(typeConfigs->typeSpecFlags[entryIndex]) &
-                    ResTable_typeSpec::SPEC_OVERLAYABLE) == 0) {
-                ++forcedOverlayCount;
-            }
-
-            matchingResources.Add(target_resid, overlay_resid);
+            typeMapping->add(target_resid, overlay_resid);
         }
     }
 
-    if (matchingResources.typeMappings.empty()) {
+    if (typeMapping->empty()) {
         ALOGE("idmap: no matching resources");
         return UNKNOWN_ERROR;
     }
 
-    matchingResources.FixPadding();
+    const IdmapMatchingResources matchingResources(std::move(typeMapping));
 
     // write idmap
     *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES; // magic, version, target and overlay crc
     *outSize += 2 * sizeof(uint16_t); // target package id, type count
-    const auto typesEnd = matchingResources.typeMappings.cend();
-    for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
+    auto fixedTypeMapping = matchingResources.getTypeMapping();
+    const auto typesEnd = fixedTypeMapping.cend();
+    for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
         *outSize += 4 * sizeof(uint16_t); // target type, overlay type, entry count, entry offset
-        *outSize += matchingResources.numberOfEntriesIncludingPadding[ti->first] *
+        *outSize += matchingResources.getNumberOfEntriesIncludingPadding(ti->first) *
             sizeof(uint32_t); // entries
     }
     if ((*outData = malloc(*outSize)) == NULL) {
@@ -7190,11 +7214,11 @@
     uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
     *typeData++ = htods(targetPackageStruct->id); // write: target package id
     *typeData++ =
-        htods(static_cast<uint16_t>(matchingResources.typeMappings.size())); // write: type count
+        htods(static_cast<uint16_t>(fixedTypeMapping.size())); // write: type count
 
     // write idmap data
-    for (auto ti = matchingResources.typeMappings.cbegin(); ti != typesEnd; ++ti) {
-        const size_t entryCount = matchingResources.numberOfEntriesIncludingPadding[ti->first];
+    for (auto ti = fixedTypeMapping.cbegin(); ti != typesEnd; ++ti) {
+        const size_t entryCount = matchingResources.getNumberOfEntriesIncludingPadding(ti->first);
         auto ei = ti->second.cbegin();
         *typeData++ = htods(Res_GETTYPE(ei->first) + 1); // write: target type id
         *typeData++ = htods(Res_GETTYPE(ei->second) + 1); // write: overlay type id
@@ -7202,7 +7226,7 @@
         *typeData++ = htods(Res_GETENTRY(ei->first)); // write: (target) entry offset
         uint32_t *entryData = reinterpret_cast<uint32_t*>(typeData);
         for (; ei != ti->second.cend(); ++ei) {
-            const size_t padding = matchingResources.entryPadding[ei->first];
+            const size_t padding = matchingResources.getPadding(ei->first);
             for (size_t i = 0; i < padding; ++i) {
                 *entryData++ = htodl(0xffffffff); // write: padding
             }
@@ -7211,10 +7235,6 @@
         typeData += entryCount * 2;
     }
 
-    if (forcedOverlayCount > 0) {
-        ALOGW("idmap: overlaid %zu resources not marked overlayable", forcedOverlayCount);
-    }
-
     return NO_ERROR;
 }
 
diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h
index 99a52dc..a0f2343 100644
--- a/libs/androidfw/include/androidfw/Chunk.h
+++ b/libs/androidfw/include/androidfw/Chunk.h
@@ -89,7 +89,9 @@
         len_(len),
         last_error_(nullptr) {
     CHECK(next_chunk_ != nullptr) << "data can't be nullptr";
-    VerifyNextChunk();
+    if (len_ != 0) {
+      VerifyNextChunk();
+    }
   }
 
   Chunk Next();
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 349b379..8c5c3b7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -20,6 +20,7 @@
 #include <memory>
 #include <set>
 #include <vector>
+#include <unordered_set>
 
 #include "android-base/macros.h"
 
@@ -76,6 +77,10 @@
 // TypeSpecPtr is a managed pointer that knows how to delete itself.
 using TypeSpecPtr = util::unique_cptr<TypeSpec>;
 
+struct OverlayableInfo {
+  uint32_t policy_flags;
+};
+
 class LoadedPackage {
  public:
   class iterator {
@@ -216,6 +221,18 @@
     }
   }
 
+  // Retrieve the overlayable properties of the specified resource. If the resource is not
+  // overlayable, this will return a null pointer.
+  const OverlayableInfo* GetOverlayableInfo(uint32_t resid) const {
+    for (const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>& overlayable_info_ids
+        : overlayable_infos_) {
+      if (overlayable_info_ids.second.find(resid) != overlayable_info_ids.second.end()) {
+        return &overlayable_info_ids.first;
+      }
+    }
+    return nullptr;
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
 
@@ -233,6 +250,7 @@
   ByteBucketArray<TypeSpecPtr> type_specs_;
   ByteBucketArray<uint32_t> resource_ids_;
   std::vector<DynamicPackageEntry> dynamic_package_map_;
+  std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
 };
 
 // Read-only view into a resource table. This class validates all data
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index ad33fcf..91261aa 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -234,7 +234,9 @@
     RES_TABLE_PACKAGE_TYPE      = 0x0200,
     RES_TABLE_TYPE_TYPE         = 0x0201,
     RES_TABLE_TYPE_SPEC_TYPE    = 0x0202,
-    RES_TABLE_LIBRARY_TYPE      = 0x0203
+    RES_TABLE_LIBRARY_TYPE      = 0x0203,
+    RES_TABLE_OVERLAYABLE_TYPE  = 0x0204,
+    RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
 };
 
 /**
@@ -1354,10 +1356,6 @@
     enum : uint32_t {
         // Additional flag indicating an entry is public.
         SPEC_PUBLIC = 0x40000000u,
-
-        // Additional flag indicating an entry is overlayable at runtime.
-        // Added in Android-P.
-        SPEC_OVERLAYABLE = 0x80000000u,
     };
 };
 
@@ -1607,6 +1605,49 @@
     uint16_t packageName[128];
 };
 
+/**
+ * Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
+ */
+struct ResTable_overlayable_header
+{
+  struct ResChunk_header header;
+};
+
+/**
+ * Holds a list of resource ids that are protected from being overlaid by a set of policies. If
+ * the overlay fulfils at least one of the policies, then the overlay can overlay the list of
+ * resources.
+ */
+struct ResTable_overlayable_policy_header
+{
+  struct ResChunk_header header;
+
+  enum PolicyFlags {
+    // Any overlay can overlay these resources.
+    POLICY_PUBLIC = 0x00000001,
+
+    // The overlay must reside of the system partition or must have existed on the system partition
+    // before an upgrade to overlay these resources.
+    POLICY_SYSTEM_PARTITION = 0x00000002,
+
+    // The overlay must reside of the vendor partition or must have existed on the vendor partition
+    // before an upgrade to overlay these resources.
+    POLICY_VENDOR_PARTITION = 0x00000004,
+
+    // The overlay must reside of the product partition or must have existed on the product
+    // partition before an upgrade to overlay these resources.
+    POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must reside of the product services partition or must have existed on the product
+    // services partition before an upgrade to overlay these resources.
+    POLICY_PRODUCT_SERVICES_PARTITION = 0x00000010,
+  };
+  uint32_t policy_flags;
+
+  // The number of ResTable_ref that follow this header.
+  uint32_t entry_count;
+};
+
 struct alignas(uint32_t) Idmap_header {
   // Always 0x504D4449 ('IDMP')
   uint32_t magic;
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index ffa4836..441356b 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -22,12 +22,14 @@
 #include "TestHelpers.h"
 #include "data/basic/R.h"
 #include "data/libclient/R.h"
+#include "data/overlayable/R.h"
 #include "data/sparse/R.h"
 #include "data/styles/R.h"
 
 namespace app = com::android::app;
 namespace basic = com::android::basic;
 namespace libclient = com::android::libclient;
+namespace overlayable = com::android::overlayable;
 namespace sparse = com::android::sparse;
 
 using ::android::base::ReadFileToString;
@@ -273,10 +275,44 @@
   ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
 }
 
-// structs with size fields (like Res_value, ResTable_entry) should be
-// backwards and forwards compatible (aka checking the size field against
-// sizeof(Res_value) might not be backwards compatible.
-TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+TEST(LoadedArscTest, LoadOverlayable) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk",
+                                      "resources.arsc", &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc =
+      LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
+                       false /*load_as_shared_library*/);
+
+  ASSERT_THAT(loaded_arsc, NotNull());
+  const LoadedPackage* package = loaded_arsc->GetPackageById(
+      get_package_id(overlayable::R::string::not_overlayable));
+
+  const OverlayableInfo* info = package->GetOverlayableInfo(
+      overlayable::R::string::not_overlayable);
+  ASSERT_THAT(info, IsNull());
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable1);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable2);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags,
+              Eq(ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable3);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags,
+              Eq(ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION
+                 | ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION));
+
+  info = package->GetOverlayableInfo(overlayable::R::string::overlayable4);
+  ASSERT_THAT(info, NotNull());
+  EXPECT_THAT(info->policy_flags, Eq(ResTable_overlayable_policy_header::POLICY_PUBLIC));
+}
 
 TEST(LoadedArscTest, ResourceIdentifierIterator) {
   std::string contents;
@@ -326,4 +362,9 @@
   ASSERT_EQ(end, iter);
 }
 
+// structs with size fields (like Res_value, ResTable_entry) should be
+// backwards and forwards compatible (aka checking the size field against
+// sizeof(Res_value) might not be backwards compatible.
+TEST(LoadedArscTest, LoadingShouldBeForwardsAndBackwardsCompatible) { ASSERT_TRUE(false); }
+
 }  // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index 33f9611..d37874d 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
similarity index 69%
rename from packages/SystemUI/res/anim/system_in.xml
rename to libs/androidfw/tests/data/overlayable/AndroidManifest.xml
index 630fd72..abc2a45 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/AndroidManifest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
@@ -14,9 +14,8 @@
      limitations under the License.
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    >
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:duration="@android:integer/config_longAnimTime" 
-        />
-</set>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.overlayable">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlayable/R.h b/libs/androidfw/tests/data/overlayable/R.h
new file mode 100644
index 0000000..e46e264d
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/R.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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 TESTS_DATA_OVERLAYABLE_R_H_
+#define TESTS_DATA_OVERLAYABLE_R_H_
+
+#include <cstdint>
+
+namespace com {
+namespace android {
+namespace overlayable {
+
+struct R {
+  struct string {
+    enum : uint32_t {
+      not_overlayable = 0x7f010000,
+      overlayable1 = 0x7f010001,
+      overlayable2 = 0x7f010002,
+      overlayable3 = 0x7f010003,
+      overlayable4 = 0x7f010004,
+    };
+  };
+};
+
+}  // namespace overlayable
+}  // namespace android
+}  // namespace com
+
+#endif /* TESTS_DATA_OVERLAYABLE_R_H_ */
diff --git a/libs/androidfw/tests/data/overlayable/build b/libs/androidfw/tests/data/overlayable/build
new file mode 100755
index 0000000..98fdc51
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/build
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2018 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.
+#
+
+set -e
+
+aapt2 compile --dir res -o compiled.flata
+aapt2 link --manifest AndroidManifest.xml -o overlayable.apk compiled.flata
+rm compiled.flata
diff --git a/libs/androidfw/tests/data/overlayable/overlayable.apk b/libs/androidfw/tests/data/overlayable/overlayable.apk
new file mode 100644
index 0000000..85ab4be
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/overlayable.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
new file mode 100644
index 0000000..11aa735
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/overlayable.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+<overlayable>
+    <!-- Any overlay can overlay the value of @string/overlayable1 -->
+    <item type="string" name="overlayable1" />
+
+    <!-- Any overlay on the product or system partition can overlay the value of
+        @string/overlayable2 -->
+    <policy type="product|system">
+        <item type="string" name="overlayable2" />
+    </policy>
+
+    <!-- Any overlay can overlay the value of @string/overlayable4 -->
+    <policy type="public">
+        <item type="string" name="overlayable4" />
+    </policy>
+</overlayable>
+
+<overlayable>
+    <!-- Any overlay on the product_services, vendor, or product partition can overlay the value of
+   @string/overlayable3 -->
+    <policy type="product_services|vendor|product">
+        <item type="string" name="overlayable3" />
+    </policy>
+</overlayable>
+</resources>
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/overlayable/res/values/public.xml b/libs/androidfw/tests/data/overlayable/res/values/public.xml
new file mode 100644
index 0000000..5676d7c
--- /dev/null
+++ b/libs/androidfw/tests/data/overlayable/res/values/public.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <public type="string" name="not_overlayable" id="0x7f010000" />
+    <public type="string" name="overlayable1" id="0x7f010001" />
+    <public type="string" name="overlayable2" id="0x7f010002" />
+    <public type="string" name="overlayable3" id="0x7f010003" />
+    <public type="string" name="overlayable4" id="0x7f010004" />
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/system_in.xml b/libs/androidfw/tests/data/overlayable/res/values/values.xml
similarity index 61%
copy from packages/SystemUI/res/anim/system_in.xml
copy to libs/androidfw/tests/data/overlayable/res/values/values.xml
index 630fd72..a86b312 100644
--- a/packages/SystemUI/res/anim/system_in.xml
+++ b/libs/androidfw/tests/data/overlayable/res/values/values.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
@@ -14,9 +14,10 @@
      limitations under the License.
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    >
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-        android:duration="@android:integer/config_longAnimTime" 
-        />
-</set>
+<resources>
+    <string name="not_overlayable">Not overlayable</string>
+    <string name="overlayable1">Overlayable One</string>
+    <string name="overlayable2">Overlayable Two</string>
+    <string name="overlayable3">Overlayable Three</string>
+    <string name="overlayable4">Overlayable Four</string>
+</resources>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 17d2db71..da77b99 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -9,8 +9,6 @@
         "hwui_lto",
     ],
 
-    cpp_std: "c++17",
-
     cflags: [
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
@@ -45,9 +43,6 @@
     ],
 
     product_variables: {
-        device_uses_hwc2: {
-            cflags: ["-DUSE_HWC2"],
-        },
         eng: {
             lto: {
                 never: true,
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 06e937a..0cfaa8c 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -146,4 +146,4 @@
     return shouldInvert;
 }
 
-};  // namespace android::uirenderer
\ No newline at end of file
+}  // namespace android::uirenderer
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index b772e5b..3bee301 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -85,19 +85,18 @@
             mUpdateTexImage = false;
             sk_sp<SkImage> layerImage;
             SkMatrix textureTransform;
-            android_dataspace dataSpace;
             bool queueEmpty = true;
             // If the SurfaceTexture queue is in synchronous mode, need to discard all
             // but latest frame. Since we can't tell which mode it is in,
             // do this unconditionally.
             do {
-                layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+                layerImage = mSurfaceTexture->dequeueImage(textureTransform, &queueEmpty,
                         mRenderState);
             } while (layerImage.get() && (!queueEmpty));
             if (layerImage.get()) {
                 // force filtration if buffer size != layer size
                 bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
-                updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
+                updateLayer(forceFilter, textureTransform, layerImage);
             }
         }
 
@@ -109,12 +108,11 @@
 }
 
 void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
-        android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
+        const sk_sp<SkImage>& layerImage) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->getTexTransform() = textureTransform;
-    mLayer->setDataSpace(dataspace);
     mLayer->setImage(layerImage);
 }
 
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b2c5131..a91c111 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -95,7 +95,7 @@
     void detachSurfaceTexture();
 
     void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
-            android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
+                     const sk_sp<SkImage>& layerImage);
 
     void destroyLayer();
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a952cc2..dc63e5d 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -31,5 +31,5 @@
  */
 using DisplayList = skiapipeline::SkiaDisplayList;
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ba72e93..237fc62 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,5 +26,5 @@
     virtual void notify(const int64_t* buffer);
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index d920a99..75b8038 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -56,5 +56,5 @@
     std::vector<sp<FrameMetricsObserver> > mObservers;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/GlFunctorLifecycleListener.h b/libs/hwui/GlFunctorLifecycleListener.h
index 5d07b46..5adc469 100644
--- a/libs/hwui/GlFunctorLifecycleListener.h
+++ b/libs/hwui/GlFunctorLifecycleListener.h
@@ -28,5 +28,5 @@
     virtual void onGlFunctorReleased(Functor* functor) = 0;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 165fc48..a97c12c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -256,4 +256,4 @@
     return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap)));
 }
 
-};  // namespace android::uirenderer
+}  // namespace android::uirenderer
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index c0113d8..6298013 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -25,4 +25,4 @@
     static sk_sp<Bitmap> allocateHardwareBitmap(const SkBitmap& sourceBitmap);
 };
 
-};  // namespace android::uirenderer
+}  // namespace android::uirenderer
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index e7ae767..ccbb6c1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -82,7 +82,6 @@
 JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) {
     mGlobalData = globalData;
     nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps);
-#if USE_HWC2
     nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms);
     nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset;
     // There are two different offset cases. If the offsetDelta is positive
@@ -96,7 +95,6 @@
         // return due to the staggering of VSYNC-app & VSYNC-sf.
         mDequeueTimeForgiveness = offsetDelta + 4_ms;
     }
-#endif
     setFrameInterval(frameIntervalNanos);
 }
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 32aaa54..a15ff22 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -33,7 +33,6 @@
     // TODO: This is a violation of Android's typical ref counting, but it
     // preserves the old inc/dec ref locations. This should be changed...
     incStrong(nullptr);
-    buildColorSpaceWithFilter();
     renderState.registerLayer(this);
     texTransform.setIdentity();
     transform.setIdentity();
@@ -43,36 +42,6 @@
     mRenderState.unregisterLayer(this);
 }
 
-void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
-    if (filter != mColorFilter) {
-        mColorFilter = filter;
-        buildColorSpaceWithFilter();
-    }
-}
-
-void Layer::setDataSpace(android_dataspace dataspace) {
-    if (dataspace != mCurrentDataspace) {
-        mCurrentDataspace = dataspace;
-        buildColorSpaceWithFilter();
-    }
-}
-
-void Layer::buildColorSpaceWithFilter() {
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
-
-    if (mColorFilter && colorSpaceFilter) {
-        mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
-    } else if (colorSpaceFilter) {
-        mColorSpaceWithFilter = colorSpaceFilter;
-    } else {
-        mColorSpaceWithFilter = mColorFilter;
-    }
-}
-
 void Layer::postDecStrong() {
     mRenderState.postDecStrong(this);
 }
@@ -85,5 +54,5 @@
     }
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e4f96e9..ea3bfc9 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -69,15 +69,9 @@
 
     SkBlendMode getMode() const;
 
-    inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+    inline sk_sp<SkColorFilter> getColorFilter() const { return mColorFilter; }
 
-    void setColorFilter(sk_sp<SkColorFilter> filter);
-
-    void setDataSpace(android_dataspace dataspace);
-
-    void setColorSpace(sk_sp<SkColorSpace> colorSpace);
-
-    inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
+    void setColorFilter(sk_sp<SkColorFilter> filter) { mColorFilter = filter; };
 
     inline SkMatrix& getTexTransform() { return texTransform; }
 
@@ -98,24 +92,12 @@
     RenderState& mRenderState;
 
 private:
-    void buildColorSpaceWithFilter();
-
     /**
      * Color filter used to draw this layer. Optional.
      */
     sk_sp<SkColorFilter> mColorFilter;
 
     /**
-     * Colorspace of the contents of the layer. Optional.
-     */
-    android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
-
-    /**
-     * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
-     */
-    sk_sp<SkColorFilter> mColorSpaceWithFilter;
-
-    /**
      * Indicates raster data backing the layer is scaled, requiring filtration.
      */
     bool forceFilter = false;
@@ -162,5 +144,5 @@
 
 };  // struct Layer
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h
index 6857999..2c63af6 100644
--- a/libs/hwui/LayerUpdateQueue.h
+++ b/libs/hwui/LayerUpdateQueue.h
@@ -50,7 +50,7 @@
     std::vector<Entry> mEntries;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_LAYER_UPDATE_QUEUE_H
diff --git a/libs/hwui/Lighting.h b/libs/hwui/Lighting.h
index d972c21..ccfbb93 100644
--- a/libs/hwui/Lighting.h
+++ b/libs/hwui/Lighting.h
@@ -34,5 +34,5 @@
     uint8_t spotShadowAlpha;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index d84ed32..d0dbff03 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -526,5 +526,5 @@
     ALOGD("]");
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 1b5cb60..b33cfe2 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -245,5 +245,5 @@
 
 typedef Matrix4 mat4;
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/NinePatchUtils.h b/libs/hwui/NinePatchUtils.h
index 082e95f..86d3cb9 100644
--- a/libs/hwui/NinePatchUtils.h
+++ b/libs/hwui/NinePatchUtils.h
@@ -103,5 +103,5 @@
     }
 }
 
-};  // namespace NinePatchUtils
-};  // namespace android
+}  // namespace NinePatchUtils
+}  // namespace android
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index ad599e9..808921d 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -304,5 +304,5 @@
     return;
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 474eb97..f5bebce 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -46,6 +46,6 @@
     static void validateVerbAndPoints(char verb, size_t points, ParseResult* result);
 };
 
-};      // namespace uirenderer
-};      // namespace android
+}      // namespace uirenderer
+}      // namespace android
 #endif  // ANDROID_HWUI_PATHPARSER_H
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 3f2c616..4a3e10c 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -223,5 +223,5 @@
     sRenderPipelineType = type;
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 542bc71..da53f66 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -289,7 +289,7 @@
     static RenderPipelineType sRenderPipelineType;
 };  // class Caches
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_PROPERTIES_H
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 80f2b57..2a48837 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -66,13 +66,10 @@
 
     sk_sp<SkColorSpace> colorSpace =
             DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
     sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
-            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
-    return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+            reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()),
+            kPremul_SkAlphaType, colorSpace);
+    return copyImageInto(image, texTransform, srcRect, bitmap);
 }
 
 CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
@@ -83,20 +80,7 @@
     transform.loadScale(1, -1, 1);
     transform.translate(0, -1);
 
-    // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
-    // TODO: When this was attempted, it resulted in instability.
-    sk_sp<SkColorFilter> colorSpaceFilter;
-    sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
-    if (colorSpace && !colorSpace->isSRGB()) {
-        colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
-    }
-    sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
-            reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
-
-    // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
-    // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
-    // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
-    return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+    return copyImageInto(hwBitmap->makeImage(), transform, srcRect, bitmap);
 }
 
 CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
@@ -118,8 +102,7 @@
     return copyResult;
 }
 
-CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
-                                   sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
                                    const Rect& srcRect, SkBitmap* bitmap) {
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
         mRenderThread.requireGlContext();
@@ -157,11 +140,7 @@
         return copyResult;
     }
 
-    // See Readback::copyLayerInto for an overview of color space conversion.
-    // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
-    // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
-    // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
-    Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+    Layer layer(mRenderThread.renderState(), nullptr, 255, SkBlendMode::kSrc);
     bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
                          MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
     layer.setForceFilter(!disableFilter);
@@ -177,38 +156,6 @@
 
 bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
                              SkBitmap* bitmap) {
-    /*
-     * In the past only TextureView readback was setting the temporary surface color space to null.
-     * Now all 3 readback flows are drawing into a SkSurface with null color space.
-     * At readback there are 3 options to convert the source image color space to the destination
-     * color space requested in "bitmap->info().colorSpace()":
-     * 1. Set color space for temporary surface render target to null (disables color management),
-     *    colorspace tag from source SkImage is ignored by Skia,
-     *    convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
-     *    do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
-     *    read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
-     *
-     * 2. Set color space for temporary surface render target to SRGB (not nullptr),
-     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
-     *    convert SkImage to SRGB at draw time with drawImage (no filters),
-     *    do a readback from temporary SkSurface, which will do a color conversion from SRGB to
-     *    bitmap->info().colorSpace() on the CPU.
-     *
-     * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
-     *    colorspace tag on the source SkImage is used by Skia to enable conversion,
-     *    convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
-     *    do a readback from SkSurface, which will not do any color conversion, because
-     *    surface was created with the same color space as the "bitmap".
-     *
-     * Option 1 is used for all readback flows.
-     * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
-     * linear blending.
-     * TODO: evaluate if options 2 or 3 for color space conversion are better.
-     */
-
-    // drop the colorSpace from the temporary surface.
-    SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
     /* This intermediate surface is present to work around a bug in SwiftShader that
      * prevents us from reading the contents of the layer's texture directly. The
      * workaround involves first rendering that texture into an intermediate buffer and
@@ -217,70 +164,44 @@
      * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
      */
     sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
-                                                              SkBudgeted::kYes, surfaceInfo);
+                                                              SkBudgeted::kYes, bitmap->info());
 
+    // if we can't generate a GPU surface that matches the destination bitmap (e.g. 565) then we
+    // attempt to do the intermediate rendering step in 8888
     if (!tmpSurface.get()) {
-        surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
         tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
-                                                 surfaceInfo);
+                                                 tmpInfo);
         if (!tmpSurface.get()) {
-            ALOGW("Unable to readback GPU contents into the provided bitmap");
+            ALOGW("Unable to generate GPU buffer in a format compatible with the provided bitmap");
             return false;
         }
     }
 
-    if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
-                                               tmpSurface->getCanvas(), layer, srcRect, dstRect,
-                                               false)) {
-        // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
-        // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
-        // disabled color conversion.
-        SkColorSpace* destColorSpace = bitmap->info().colorSpace();
-        SkBitmap tempSRGBBitmap;
-        SkBitmap tmpN32Bitmap;
-        SkBitmap* bitmapInSRGB;
-        if (destColorSpace && !destColorSpace->isSRGB()) {
-            tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
-            bitmapInSRGB = &tempSRGBBitmap;  // Need to convert latter from SRGB to non-SRGB.
-        } else {
-            bitmapInSRGB = bitmap;  // No need for color conversion - write directly into output.
-        }
-        bool success = false;
+    if (!skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+                                                tmpSurface->getCanvas(), layer, srcRect, dstRect,
+                                                false)) {
+        ALOGW("Unable to draw content from GPU into the provided bitmap");
+        return false;
+    }
 
-        // TODO: does any of the readbacks below clamp F16 exSRGB?
-        // Readback into a SRGB SkBitmap.
-        if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
-                                   bitmapInSRGB->rowBytes(), 0, 0)) {
-            success = true;
-        } else {
-            // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
-            // 8888 and then convert that into the destination format before giving up.
-            SkImageInfo bitmapInfo =
-                    SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
-                                         SkColorSpace::MakeSRGB());
-            if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
-                tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
-                                       tmpN32Bitmap.rowBytes(), 0, 0)) {
-                success = true;
-                bitmapInSRGB = &tmpN32Bitmap;
-            }
-        }
-
-        if (success) {
-            if (bitmapInSRGB != bitmap) {
-                // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
-                // destination bitmap color format if needed.
-                if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
-                                              bitmap->rowBytes(), 0, 0)) {
-                    return false;
-                }
-            }
-            bitmap->notifyPixelsChanged();
-            return true;
+    if (!tmpSurface->readPixels(*bitmap, 0, 0)) {
+        // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+        // 8888 and then convert that into the destination format before giving up.
+        SkBitmap tmpBitmap;
+        SkImageInfo tmpInfo = bitmap->info().makeColorType(SkColorType::kN32_SkColorType);
+        if (bitmap->info().colorType() == SkColorType::kN32_SkColorType ||
+                !tmpBitmap.tryAllocPixels(tmpInfo) ||
+                !tmpSurface->readPixels(tmpBitmap, 0, 0) ||
+                !tmpBitmap.readPixels(bitmap->info(), bitmap->getPixels(),
+                                      bitmap->rowBytes(), 0, 0)) {
+            ALOGW("Unable to convert content into the provided bitmap");
+            return false;
         }
     }
 
-    return false;
+    bitmap->notifyPixelsChanged();
+    return true;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index d9e10ce..e86a813 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -54,8 +54,8 @@
     CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
 private:
-    CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
-                             Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+    CopyResult copyImageInto(const sk_sp<SkImage>& image, Matrix4& texTransform,
+                             const Rect& srcRect, SkBitmap* bitmap);
 
     bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
                        SkBitmap* bitmap);
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f928de9..c63e449 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -1028,5 +1028,5 @@
     fDL->drawVectorDrawable(tree);
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 099e0be..08cfc62 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -216,5 +216,5 @@
     DisplayListData* fDL;
 };
 
-};  // namespace uirenderer
-};  // namespace android
\ No newline at end of file
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 0715187..d6362ef 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -262,5 +262,5 @@
     }
 };  // class Rect
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d2a8f02..4a63910 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,6 +29,7 @@
 
 #include <SkPathOps.h>
 #include <algorithm>
+#include <atomic>
 #include <sstream>
 #include <string>
 
@@ -47,8 +48,14 @@
     TreeInfo* mTreeInfo;
 };
 
+static int64_t generateId() {
+    static std::atomic<int64_t> sNextId{1};
+    return sNextId++;
+}
+
 RenderNode::RenderNode()
-        : mDirtyPropertyFields(0)
+        : mUniqueId(generateId())
+        , mDirtyPropertyFields(0)
         , mNeedsDisplayListSync(false)
         , mDisplayList(nullptr)
         , mStagingDisplayList(nullptr)
@@ -444,5 +451,38 @@
     return &mClippedOutlineCache.clippedOutline;
 }
 
+using StringBuffer = FatVector<char, 128>;
+
+template <typename... T>
+static void format(StringBuffer& buffer, const std::string_view& format, T... args) {
+    buffer.resize(buffer.capacity());
+    while (1) {
+        int needed = snprintf(buffer.data(), buffer.size(),
+                format.data(), std::forward<T>(args)...);
+        if (needed < 0) {
+            buffer[0] = '\0';
+            buffer.resize(1);
+            return;
+        }
+        if (needed < buffer.size()) {
+            buffer.resize(needed + 1);
+            return;
+        }
+        buffer.resize(buffer.size() * 2);
+    }
+}
+
+void RenderNode::markDrawStart(SkCanvas& canvas) {
+    StringBuffer buffer;
+    format(buffer, "RenderNode(id=%d, name='%s')", uniqueId(), getName());
+    canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
+void RenderNode::markDrawEnd(SkCanvas& canvas) {
+    StringBuffer buffer;
+    format(buffer, "/RenderNode(id=%d, name='%s')", uniqueId(), getName());
+    canvas.drawAnnotation(SkRect::MakeWH(getWidth(), getHeight()), buffer.data(), nullptr);
+}
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index be0b46b..6060123 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -213,6 +213,11 @@
 
     UsageHint usageHint() const { return mUsageHint; }
 
+    int64_t uniqueId() const { return mUniqueId; }
+
+    void markDrawStart(SkCanvas& canvas);
+    void markDrawEnd(SkCanvas& canvas);
+
 private:
     void computeOrderingImpl(RenderNodeOp* opState,
                              std::vector<RenderNodeOp*>* compositedChildrenOfProjectionSurface,
@@ -233,6 +238,7 @@
     void incParentRefCount() { mParentCount++; }
     void decParentRefCount(TreeObserver& observer, TreeInfo* info = nullptr);
 
+    const int64_t mUniqueId;
     String8 mName;
     sp<VirtualLightRefBase> mUserContext;
 
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9a15ff2..6be7ef7 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -27,7 +27,6 @@
 #include <SkAnimatedImage.h>
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
-#include <SkColorSpaceXformCanvas.h>
 #include <SkDeque.h>
 #include <SkDrawable.h>
 #include <SkFont.h>
@@ -61,19 +60,8 @@
 SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
 
 SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
-    sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
-    mCanvasOwned =
-            std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    if (cs.get() == nullptr || cs->isSRGB()) {
-        mCanvas = mCanvasOwned.get();
-    } else {
-        /** The wrapper is needed if we are drawing into a non-sRGB destination, since
-         *  we need to transform all colors (not just bitmaps via filters) into the
-         *  destination's colorspace.
-         */
-        mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
-        mCanvas = mCanvasWrapper.get();
-    }
+    mCanvasOwned = std::unique_ptr<SkCanvas>(new SkCanvas(bitmap));
+    mCanvas = mCanvasOwned.get();
 }
 
 SkiaCanvas::~SkiaCanvas() {}
@@ -82,7 +70,6 @@
     if (mCanvas != skiaCanvas) {
         mCanvas = skiaCanvas;
         mCanvasOwned.reset();
-        mCanvasWrapper.reset();
     }
     mSaveStack.reset(nullptr);
 }
@@ -92,18 +79,9 @@
 // ----------------------------------------------------------------------------
 
 void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
-    sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
-    std::unique_ptr<SkCanvas> newCanvas =
-            std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    std::unique_ptr<SkCanvas> newCanvasWrapper;
-    if (cs.get() != nullptr && !cs->isSRGB()) {
-        newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
-    }
-
     // deletes the previously owned canvas (if any)
-    mCanvasOwned = std::move(newCanvas);
-    mCanvasWrapper = std::move(newCanvasWrapper);
-    mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
+    mCanvasOwned.reset(new SkCanvas(bitmap));
+    mCanvas = mCanvasOwned.get();
 
     // clean up the old save stack
     mSaveStack.reset(nullptr);
@@ -548,40 +526,14 @@
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-SkiaCanvas::PaintCoW&& SkiaCanvas::filterBitmap(PaintCoW&& paint,
-                                                sk_sp<SkColorFilter> colorSpaceFilter) const {
-    /* We don't apply the colorSpace filter if this canvas is already wrapped with
-     * a SkColorSpaceXformCanvas since it already takes care of converting the
-     * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
-     * should only be used if this canvas is backed by a surface/bitmap that is known
-     * to have a non-sRGB colorspace.
-     */
-    if (!mCanvasWrapper && colorSpaceFilter) {
-        SkPaint& tmpPaint = paint.writeable();
-        if (tmpPaint.getColorFilter()) {
-            tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(tmpPaint.refColorFilter(),
-                                                                     std::move(colorSpaceFilter)));
-            LOG_ALWAYS_FATAL_IF(!tmpPaint.getColorFilter());
-        } else {
-            tmpPaint.setColorFilter(std::move(colorSpaceFilter));
-        }
-    }
-    return filterPaint(std::move(paint));
-}
-
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
     SkAutoCanvasRestore acr(mCanvas, true);
     mCanvas->concat(matrix);
-
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint));
 }
 
 void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight,
@@ -590,9 +542,7 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+    mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint),
                            SkCanvas::kFast_SrcRectConstraint);
 }
 
@@ -674,13 +624,9 @@
     PaintCoW paintCoW(paint);
     SkPaint& tmpPaint = paintCoW.writeable();
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+    sk_sp<SkImage> image = bitmap.makeImage();
     sk_sp<SkShader> shader =
             image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-    if (colorFilter) {
-        shader = shader->makeWithColorFilter(std::move(colorFilter));
-    }
     tmpPaint.setShader(std::move(shader));
 
     mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate,
@@ -711,10 +657,7 @@
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mCanvas->drawImageLattice(image.get(), lattice, dst,
-                              filterBitmap(paint, std::move(colorFilter)));
+    mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint));
 }
 
 double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) {
@@ -738,7 +681,7 @@
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+    SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
     // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
     // older.
     if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0 &&
@@ -765,7 +708,7 @@
     if (mPaintFilter) {
         mPaintFilter->filter(&paintCopy);
     }
-    SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+    SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
 
     const int N = end - start;
     SkAutoSTMalloc<1024, uint8_t> storage(N * (sizeof(uint16_t) + sizeof(SkRSXform)));
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 3a877cf..24d9c08 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -232,7 +232,6 @@
 
     class Clip;
 
-    std::unique_ptr<SkCanvas> mCanvasWrapper;  // might own a wrapper on the canvas
     std::unique_ptr<SkCanvas> mCanvasOwned;    // might own a canvas we allocated
     SkCanvas* mCanvas;                         // we do NOT own this canvas, it must survive us
                                                // unless it is the same as mCanvasOwned.get()
diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h
index b495e33..833ca4a 100644
--- a/libs/hwui/UvMapper.h
+++ b/libs/hwui/UvMapper.h
@@ -124,7 +124,7 @@
     float mMaxV;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_UV_MAPPER_H
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index d2c15ad..e6eea1c 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -113,7 +113,7 @@
     }
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_VECTOR_H
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 6cf04bf..dd62bbb 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -691,7 +691,7 @@
     return BitmapPalette::Unknown;
 }
 
-};  // namespace VectorDrawable
+}  // namespace VectorDrawable
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index f091277..28cabb9 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -73,7 +73,7 @@
 
 REQUIRE_COMPATIBLE_LAYOUT(TextureVertex);
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_VERTEX_H
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 613cf4a..6543a22 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -174,7 +174,7 @@
     void (*mCleanupIndexMethod)(void*);
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_VERTEX_BUFFER_H
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 75a6e72..6c77f9e 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
 
 #include <SkCanvas.h>
 #include <SkImagePriv.h>
-#include <SkToSRGBColorFilter.h>
 
 #include <SkHighContrastFilter.h>
 #include <limits>
@@ -287,14 +286,8 @@
 
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
     if (isHardware()) {
-        outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
-                                                 info().colorType(), info().alphaType(), nullptr));
+        outBitmap->allocPixels(mInfo);
         uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
-        if (mInfo.colorSpace()) {
-            sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
-            outBitmap->setInfo(mInfo);
-            outBitmap->setPixelRef(std::move(pixelRef), 0, 0);
-        }
         return;
     }
     outBitmap->setInfo(mInfo, rowBytes());
@@ -313,7 +306,7 @@
     return nullptr;
 }
 
-sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) {
+sk_sp<SkImage> Bitmap::makeImage() {
     sk_sp<SkImage> image = mImage;
     if (!image) {
         SkASSERT(!isHardware());
@@ -325,9 +318,6 @@
         // TODO: refactor Bitmap to not derive from SkPixelRef, which would allow caching here.
         image = SkMakeImageFromRasterBitmap(skiaBitmap, kNever_SkCopyPixelsMode);
     }
-    if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) {
-        *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace());
-    }
     return image;
 }
 
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 238c764..d446377 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -105,14 +105,8 @@
      * Creates or returns a cached SkImage and is safe to be invoked from either
      * the UI or RenderThread.
      *
-     * @param outputColorFilter is a required param that will be populated by
-     *     this function if the bitmap's colorspace is not sRGB. If populated the
-     *     filter will convert colors from the bitmaps colorspace into sRGB. It
-     *     is the callers responsibility to use this colorFilter when drawing
-     *     this image into any destination that is presumed to be sRGB (i.e. a
-     *     buffer that has no colorspace defined).
      */
-    sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter);
+    sk_sp<SkImage> makeImage();
 
     static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes);
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index e99742b..a5f21d8 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -76,8 +76,8 @@
 namespace uirenderer {
 namespace VectorDrawable {
 class Tree;
-};
-};
+}
+}
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
 typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc;
@@ -318,4 +318,4 @@
     friend class DrawTextOnPathFunctor;
 };
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 769fce4..84292c8 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -43,7 +43,7 @@
 static void MinikinFontSkia_SetSkiaPaint(const minikin::MinikinFont* font, SkPaint* skPaint,
                                          const minikin::MinikinPaint& paint,
                                          const minikin::FontFakery& fakery) {
-    skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    skPaint->setTextEncoding(kGlyphID_SkTextEncoding);
     skPaint->setTextSize(paint.size);
     skPaint->setTextScaleX(paint.scaleX);
     skPaint->setTextSkewX(paint.skewX);
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index efef6de..bf19655 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -79,6 +79,6 @@
     sp<uirenderer::CanvasPropertyPaint> mPaint;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index e4ba13d..2062194 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -172,6 +172,6 @@
     std::string mIdent;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/FunctorDrawable.h b/libs/hwui/pipeline/skia/FunctorDrawable.h
index 162d137..af3a056 100644
--- a/libs/hwui/pipeline/skia/FunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/FunctorDrawable.h
@@ -48,6 +48,6 @@
     const SkRect mBounds;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 90d5e71..4a87e75 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -216,6 +216,6 @@
     }
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.h b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
index b06f7f0..215979c 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.h
@@ -41,6 +41,6 @@
     void onDraw(SkCanvas* canvas) override;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 0cd6406..f08ac17 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -89,7 +89,7 @@
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
-        paint.setColorFilter(layer->getColorSpaceWithFilter());
+        paint.setColorFilter(layer->getColorFilter());
         const bool nonIdentityMatrix = !matrix.isIdentity();
         if (nonIdentityMatrix) {
             canvas->save();
@@ -145,6 +145,6 @@
     return layerImage != nullptr;
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 5c12590..95dc6d0 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -45,6 +45,6 @@
     sp<DeferredLayerUpdater> mLayerUpdater;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index ea14d11..4494cb0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -115,12 +115,26 @@
     }
 }
 
+class MarkDraw {
+public:
+    explicit MarkDraw(SkCanvas& canvas, RenderNode& node) : mCanvas(canvas), mNode(node) {
+        if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+            mNode.markDrawStart(mCanvas);
+        }
+    }
+    ~MarkDraw() {
+        if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
+            mNode.markDrawEnd(mCanvas);
+        }
+    }
+private:
+    SkCanvas& mCanvas;
+    RenderNode& mNode;
+};
+
 void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
     RenderNode* renderNode = mRenderNode.get();
-    if (CC_UNLIKELY(Properties::skpCaptureEnabled)) {
-        SkRect dimensions = SkRect::MakeWH(renderNode->getWidth(), renderNode->getHeight());
-        canvas->drawAnnotation(dimensions, renderNode->getName(), nullptr);
-    }
+    MarkDraw _marker{*canvas, *renderNode};
 
     // We only respect the nothingToDraw check when we are composing a layer. This
     // ensures that we paint the layer even if it is not currently visible in the
@@ -318,6 +332,6 @@
     }
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index d746978..6ba8e59 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -150,6 +150,6 @@
     SkiaDisplayList* mProjectedDisplayList = nullptr;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
index dba97fe..0a3c8f4 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -211,6 +211,6 @@
             casterAlpha < 1.0f ? SkShadowFlags::kTransparentOccluder_ShadowFlag : 0);
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index 26cfa90..cfc0f9b 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -74,6 +74,6 @@
     StartReorderBarrierDrawable* mStartBarrier;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index 3890513..ac6f6a3 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -141,6 +141,6 @@
     mDisplayList.draw(&canvas);
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index ac7bb7b..d7879e7 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -36,7 +36,7 @@
 
 namespace VectorDrawable {
 class Tree;
-};
+}
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
 namespace skiapipeline {
@@ -179,6 +179,6 @@
     SkMatrix mParentMatrix;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 6ae5999..142bca9 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -97,7 +97,7 @@
     SkASSERT(mRenderThread.getGrContext() != nullptr);
     sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
             mRenderThread.getGrContext(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType,
-            nullptr, &props));
+            mSurfaceColorSpace, &props));
 
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
@@ -176,6 +176,7 @@
     } else if (colorMode == ColorMode::WideColorGamut) {
         mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
     }
+    mSurfaceColorSpace = SkColorSpace::MakeSRGB();
 
     if (mEglSurface != EGL_NO_SURFACE) {
         const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 2dfe7c7..7a255c1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -169,7 +169,7 @@
     if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
         SkImageInfo info;
         info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(),
-                                 kPremul_SkAlphaType);
+                                 kPremul_SkAlphaType, getSurfaceColorSpace());
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
         node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
@@ -204,8 +204,7 @@
     GrContext* context = thread.getGrContext();
     if (context) {
         ATRACE_FORMAT("Bitmap#prepareToDraw %dx%d", bitmap->width(), bitmap->height());
-        sk_sp<SkColorFilter> colorFilter;
-        auto image = bitmap->makeImage(&colorFilter);
+        auto image = bitmap->makeImage();
         if (image.get() && !bitmap->isHardware()) {
             SkImage_pinAsTexture(image.get(), context);
             SkImage_unpinAsTexture(image.get(), context);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 45022e7..b682ab0 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -179,9 +179,8 @@
 }
 
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) {
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, left, top, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImage(image, left, top, filterPaint(paint), bitmap.palette());
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
     // it is not safe to store a raw SkImage pointer, because the image object will be destroyed
     // when this function ends.
@@ -194,9 +193,8 @@
     SkAutoCanvasRestore acr(&mRecorder, true);
     concat(matrix);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImage(image, 0, 0, filterBitmap(paint, std::move(colorFilter)), bitmap.palette());
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImage(image, 0, 0, filterPaint(paint), bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
@@ -208,9 +206,8 @@
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
 
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint, std::move(colorFilter)),
+    sk_sp<SkImage> image = bitmap.makeImage();
+    mRecorder.drawImageRect(image, srcRect, dstRect, filterPaint(paint),
                             SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
         !dstRect.isEmpty()) {
@@ -247,10 +244,9 @@
     if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
         filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
     }
-    sk_sp<SkColorFilter> colorFilter;
-    sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+    sk_sp<SkImage> image = bitmap.makeImage();
     mRecorder.drawImageLattice(image, lattice, dst,
-                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+                               filterPaint(std::move(filteredPaint)),
                                bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
@@ -263,6 +259,6 @@
     return 0;
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 988728d..d6107a9 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -92,6 +92,6 @@
     PaintCoW&& filterBitmap(PaintCoW&& paint, sk_sp<SkColorFilter> colorSpaceFilter);
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a494e49..3607b23 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -122,8 +122,9 @@
         mVkSurface = nullptr;
     }
 
+    mSurfaceColorSpace = SkColorSpace::MakeSRGB();
     if (surface) {
-        mVkSurface = mVkManager.createSurface(surface, colorMode);
+        mVkSurface = mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace);
     }
 
     if (colorMode == ColorMode::SRGB) {
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index a594206..004a558 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -219,6 +219,6 @@
     });
 }
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
index 3269cfb..8fe52c5 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.h
@@ -51,6 +51,6 @@
     SkImageInfo mFBInfo;
 };
 
-};  // namespace skiapipeline
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
index efa9da2..9e1bb8e 100644
--- a/libs/hwui/private/hwui/DrawGlInfo.h
+++ b/libs/hwui/private/hwui/DrawGlInfo.h
@@ -83,7 +83,7 @@
     };
 };  // struct DrawGlInfo
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_DRAW_GL_INFO_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f1a522e..6869972 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -323,25 +323,6 @@
             // the deadline for RT animations
             info.out.canDrawThisFrame = false;
         }
-        /* This logic exists to try and recover from a display latch miss, which essentially
-         * results in the bufferqueue being double-buffered instead of triple-buffered.
-         * SurfaceFlinger itself now tries to handle & recover from this situation, so this
-         * logic should no longer be necessary. As it's occasionally triggering when
-         * undesired disable it.
-         * TODO: Remove this entirely if the results are solid.
-        else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3 ||
-                   (latestVsync - mLastDropVsync) < 500_ms) {
-            // It's been several frame intervals, assume the buffer queue is fine
-            // or the last drop was too recent
-            info.out.canDrawThisFrame = true;
-        } else {
-            info.out.canDrawThisFrame = !isSwapChainStuffed();
-            if (!info.out.canDrawThisFrame) {
-                // dropping frame
-                mLastDropVsync = mRenderThread.timeLord().latestVsync();
-            }
-        }
-        */
     } else {
         info.out.canDrawThisFrame = true;
     }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6668c58..d9b789f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -49,7 +49,7 @@
     Reset = 1 << 1,
     JankStats = 1 << 2,
 };
-};
+}
 
 /*
  * RenderProxy is strictly single threaded. All methods must be invoked on the owning
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index d84ec85..2abb3d5 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -263,6 +263,15 @@
         tailPNext = &blend->pNext;
     }
 
+    VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature;
+    ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) malloc(
+            sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
+    LOG_ALWAYS_FATAL_IF(!ycbcrFeature);
+    ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+    ycbcrFeature->pNext = nullptr;
+    *tailPNext = ycbcrFeature;
+    tailPNext = &ycbcrFeature->pNext;
+
     // query to get the physical device features
     mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
     // this looks like it would slow things down,
@@ -463,8 +472,9 @@
     window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
     if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
         ColorMode colorMode = surface->mColorMode;
+        sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
         destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode);
+        *surfaceOut = createSurface(window, colorMode, colorSpace);
         surface = *surfaceOut;
     }
 
@@ -638,7 +648,7 @@
         imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
                 mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin,
                 surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType
-                : kRGBA_8888_SkColorType, nullptr, &props);
+                : kRGBA_8888_SkColorType, surface->mColorSpace, &props);
     }
 
     SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -735,7 +745,7 @@
     surface->mWindowWidth = extent.width;
     surface->mWindowHeight = extent.height;
 
-    uint32_t imageCount = caps.minImageCount + 2;
+    uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
     if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
         // Application must settle for fewer images than desired:
         imageCount = caps.maxImageCount;
@@ -824,14 +834,15 @@
     return true;
 }
 
-VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) {
+VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
+        sk_sp<SkColorSpace> surfaceColorSpace) {
     initialize();
 
     if (!window) {
         return nullptr;
     }
 
-    VulkanSurface* surface = new VulkanSurface(colorMode, window);
+    VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace);
 
     VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
     memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 8594a1b..d67d2c8 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -38,8 +38,8 @@
 
 class VulkanSurface {
 public:
-    VulkanSurface(ColorMode colorMode, ANativeWindow* window)
-            : mColorMode(colorMode), mNativeWindow(window) {}
+    VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace)
+            : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace) {}
 
     sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
 
@@ -79,6 +79,7 @@
     ANativeWindow* mNativeWindow;
     int mWindowWidth = 0;
     int mWindowHeight = 0;
+    sk_sp<SkColorSpace> mColorSpace;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -96,7 +97,8 @@
 
     // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
     // VulkanSurface object which is returned.
-    VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode);
+    VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
+            sk_sp<SkColorSpace> surfaceColorSpace);
 
     // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp
index c8220c6..85b3917 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.cpp
+++ b/libs/hwui/surfacetexture/EGLConsumer.cpp
@@ -672,4 +672,4 @@
     return image;
 }
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h
index eccb082..7dac3ef 100644
--- a/libs/hwui/surfacetexture/EGLConsumer.h
+++ b/libs/hwui/surfacetexture/EGLConsumer.h
@@ -308,4 +308,4 @@
     sp<EglImage> mReleasedTexImage;
 };
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
index 9ffccfb..15aec9f 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.cpp
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -22,6 +22,7 @@
 #include "renderthread/EglManager.h"
 #include "renderthread/RenderThread.h"
 #include "renderthread/VulkanManager.h"
+#include "utils/Color.h"
 
 // Macro for including the SurfaceTexture name in log messages
 #define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
@@ -44,13 +45,16 @@
     mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
 }
 
-void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
-    if (!mImage.get()) {
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer,
+                                              android_dataspace dataspace) {
+    if (!mImage.get() || dataspace != mDataspace) {
         mImage = graphicBuffer.get()
                          ? SkImage::MakeFromAHardwareBuffer(
                                    reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
-                                   kPremul_SkAlphaType)
+                                   kPremul_SkAlphaType,
+                                   uirenderer::DataSpaceToColorSpace(dataspace))
                          : nullptr;
+        mDataspace = dataspace;
     }
 }
 
@@ -66,7 +70,7 @@
             int slot = st.mCurrentTexture;
             if (slot != BufferItem::INVALID_BUFFER_SLOT) {
                 *queueEmpty = true;
-                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+                mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
                 return mImageSlots[slot].mImage;
             }
         }
@@ -145,7 +149,7 @@
     st.computeCurrentTransformMatrixLocked();
 
     *queueEmpty = false;
-    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+    mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer, item.mDataSpace);
     return mImageSlots[slot].mImage;
 }
 
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
index 31ee8db..f0e55bb 100644
--- a/libs/hwui/surfacetexture/ImageConsumer.h
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -68,18 +68,21 @@
      * ImageConsumer maintains about a BufferQueue buffer slot.
      */
     struct ImageSlot {
-        ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+        ImageSlot() : mDataspace(HAL_DATASPACE_UNKNOWN), mEglFence(EGL_NO_SYNC_KHR) {}
 
         // mImage is the SkImage created from mGraphicBuffer.
         sk_sp<SkImage> mImage;
 
+        // the dataspace associated with the current image
+        android_dataspace mDataspace;
+
         /**
          * mEglFence is the EGL sync object that must signal before the buffer
          * associated with this buffer slot may be dequeued.
          */
         EGLSyncKHR mEglFence;
 
-        void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+        void createIfNeeded(sp<GraphicBuffer> graphicBuffer, android_dataspace dataspace);
     };
 
     /**
@@ -94,4 +97,4 @@
     ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
 };
 
-}; /* namespace android */
+} /* namespace android */
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
index 4bff715..da09444 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.cpp
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -470,8 +470,7 @@
     ConsumerBase::dumpLocked(result, prefix);
 }
 
-sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
-                                            bool* queueEmpty,
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
                                             uirenderer::RenderState& renderState) {
     Mutex::Autolock _l(mMutex);
 
@@ -488,9 +487,8 @@
     auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
     if (image.get()) {
         uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
-        dataSpace = mCurrentDataSpace;
     }
     return image;
 }
 
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
index db392a9..b5d136f 100644
--- a/libs/hwui/surfacetexture/SurfaceTexture.h
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -258,8 +258,8 @@
      */
     status_t attachToContext(uint32_t tex);
 
-    sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
-                                bool* queueEmpty, uirenderer::RenderState& renderState);
+    sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, bool* queueEmpty,
+                                uirenderer::RenderState& renderState);
 
     /**
      * attachToView attaches a SurfaceTexture that is currently in the
@@ -449,4 +449,4 @@
 };
 
 // ----------------------------------------------------------------------------
-};  // namespace android
+}  // namespace android
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 66b9b85..f812022 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,9 +72,7 @@
     layerUpdater->setTransform(&transform);
 
     // updateLayer so it's ready to draw
-    SkMatrix identity;
-    identity.setIdentity();
-    layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
+    layerUpdater->updateLayer(true, SkMatrix::I(), nullptr);
     return layerUpdater;
 }
 
@@ -83,7 +81,7 @@
     auto utf16 = asciiToUtf16(text);
     uint32_t length = strlen(text);
     SkPaint glyphPaint(paint);
-    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawText(
             utf16.get(), length,  // text buffer
             0, length,  // draw range
@@ -96,7 +94,7 @@
                                  const SkPath& path) {
     auto utf16 = asciiToUtf16(text);
     SkPaint glyphPaint(paint);
-    glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
     canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
             nullptr);
 }
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index 15039b5..ad11a1d 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -44,8 +44,7 @@
                 });
 
         SkPaint paint;
-        sk_sp<SkColorFilter> colorFilter;
-        sk_sp<SkImage> image = hwuiBitmap->makeImage(&colorFilter);
+        sk_sp<SkImage> image = hwuiBitmap->makeImage();
         sk_sp<SkShader> repeatShader =
                 image->makeShader(SkShader::TileMode::kRepeat_TileMode,
                                   SkShader::TileMode::kRepeat_TileMode, nullptr);
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index f137562..448408d 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -72,8 +72,7 @@
     void doFrame(int frameNr) override {}
 
     sk_sp<SkShader> createBitmapShader(Bitmap& bitmap) {
-        sk_sp<SkColorFilter> colorFilter;
-        sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
+        sk_sp<SkImage> image = bitmap.makeImage();
         return image->makeShader(SkShader::TileMode::kClamp_TileMode,
                                  SkShader::TileMode::kClamp_TileMode);
     }
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index c235715..210fced 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -54,8 +54,7 @@
     // create an image and pin it so that we have something with a unique key in the cache
     sk_sp<Bitmap> bitmap =
             Bitmap::allocateHeapBitmap(SkImageInfo::MakeA8(displayInfo.w, displayInfo.h));
-    sk_sp<SkColorFilter> filter;
-    sk_sp<SkImage> image = bitmap->makeImage(&filter);
+    sk_sp<SkImage> image = bitmap->makeImage();
     ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
 
     // attempt to trim all memory while we still hold strong refs
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index 6c8775b..a686979 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -43,7 +43,7 @@
     SkBitmap bitmap;
     bitmap.allocN32Pixels(16, 16);
     sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
-    layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
+    layerUpdater->updateLayer(true, scaledMatrix, layerImage);
 
     // the backing layer should now have all the properties applied.
     EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 217d63f..41714eb 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -81,5 +81,5 @@
 
     EXPECT_TRUE(queue.entries().empty());
 }
-};
-};
+}
+}
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 634ceff..f3a7648 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -53,12 +53,12 @@
     adobeBitmap->getSkBitmap(&adobeSkBitmap);
     *adobeSkBitmap.getAddr32(0, 0) = 0xFF0000F0;  // Opaque, almost fully-red
 
-    SkImageInfo info = adobeInfo.makeColorSpace(nullptr);
+    SkImageInfo info = adobeInfo.makeColorSpace(SkColorSpace::MakeSRGB());
     sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
     SkBitmap skBitmap;
     bitmap->getSkBitmap(&skBitmap);
 
-    // Create a software canvas.
+    // Create a software sRGB canvas.
     SkiaCanvas canvas(skBitmap);
     canvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     // The result should be fully red, since we convert to sRGB at draw time.
@@ -77,7 +77,7 @@
     picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
-    // Playback to an software canvas.  The result should be fully red.
+    // Playback to a software sRGB canvas.  The result should be fully red.
     canvas.asSkCanvas()->drawPicture(picture);
     ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
 }
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 02f740c..ee6beba 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -406,5 +406,5 @@
     EXPECT_TRUE(shader->unique());
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
index 8faeee6..bb750ca 100644
--- a/libs/hwui/thread/Barrier.h
+++ b/libs/hwui/thread/Barrier.h
@@ -48,7 +48,7 @@
     mutable Condition mCondition;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
index 45f3102..df53348e 100644
--- a/libs/hwui/thread/Future.h
+++ b/libs/hwui/thread/Future.h
@@ -53,7 +53,7 @@
     T mResult;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index ffcd4b6..6d33ac4 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -53,7 +53,7 @@
     mutable Condition mCondition;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_SIGNAL_H
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 276a22f..228ce19 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -48,7 +48,7 @@
     sp<Future<T> > mFuture;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_TASK_H
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 54b55e4..26ff6eb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -129,5 +129,5 @@
     mSignal.signal();
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 29b4fcd..c4c1291 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -101,7 +101,7 @@
     std::vector<sp<WorkerThread> > mThreads;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_TASK_MANAGER_H
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index 1bc5646..763d1aa 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -178,5 +178,5 @@
     }
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index bec3837..d6b41b8 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -41,7 +41,7 @@
                          int32_t width, int32_t height);
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_HWUI_BLUR_H
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 3fb6a31..dc347f6 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -221,5 +221,5 @@
             static_cast<uint8_t>(rgb.b * 255));
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index eafe2f1..8cc4d10 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -99,7 +99,7 @@
     typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_FAT_VECTOR_H
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
index fcd036c..c694e93 100644
--- a/libs/hwui/utils/GLUtils.cpp
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -76,5 +76,5 @@
     }
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.cpp b/libs/hwui/utils/LinearAllocator.cpp
index 3e5021c..8baa4b77 100644
--- a/libs/hwui/utils/LinearAllocator.cpp
+++ b/libs/hwui/utils/LinearAllocator.cpp
@@ -249,5 +249,5 @@
     ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount);
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index 03f685e..b401fcf 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -201,7 +201,7 @@
             : std::vector<T, LinearStdAllocator<T>>(allocator) {}
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif  // ANDROID_LINEARALLOCATOR_H
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
index 4bcd576..76f93cb 100644
--- a/libs/hwui/utils/Pair.h
+++ b/libs/hwui/utils/Pair.h
@@ -36,7 +36,7 @@
     inline const S& getSecond() const { return second; }
 };
 
-};  // namespace uirenderer
+}  // namespace uirenderer
 
 template <typename F, typename S>
 struct trait_trivial_ctor<uirenderer::Pair<F, S> > {
@@ -55,6 +55,6 @@
     enum { value = aggregate_traits<F, S>::has_trivial_move };
 };
 
-};  // namespace android
+}  // namespace android
 
 #endif  // ANDROID_HWUI_PAIR_H
diff --git a/libs/hwui/utils/Result.h b/libs/hwui/utils/Result.h
index 7f33f2e..bd20ba6 100644
--- a/libs/hwui/utils/Result.h
+++ b/libs/hwui/utils/Result.h
@@ -51,4 +51,4 @@
     std::variant<R, Error<E>> result;
 };
 
-}; // namespace android::uirenderer
+} // namespace android::uirenderer
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index b3e8931..081386a 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -61,7 +61,7 @@
     size_t mCount = 0;
 };
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
 
 #endif /* RINGBUFFER_H_ */
diff --git a/libs/hwui/utils/StringUtils.cpp b/libs/hwui/utils/StringUtils.cpp
index 5304b76..304982e 100644
--- a/libs/hwui/utils/StringUtils.cpp
+++ b/libs/hwui/utils/StringUtils.cpp
@@ -34,5 +34,5 @@
     return set;
 }
 
-};  // namespace uirenderer
-};  // namespace android
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/utils/TypeLogic.h b/libs/hwui/utils/TypeLogic.h
index dbdad33..1689cce 100644
--- a/libs/hwui/utils/TypeLogic.h
+++ b/libs/hwui/utils/TypeLogic.h
@@ -37,4 +37,4 @@
 template <typename D, typename S> using same_cv = copy_cv<std::remove_cv_t<D>, S>;
 template <typename D, typename S> using same_cv_t = typename same_cv<D, S>::type;
 
-}; // namespace android::uirenderer
\ No newline at end of file
+} // namespace android::uirenderer
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index f60c338..8de2ab4 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -82,6 +82,11 @@
   STATS_LOG_VALUE_TYPE type;
 };
 
+struct WorkChain {
+  std::vector<int32_t> uids;
+  std::vector<std::string> tags;
+};
+
 // Represents a parcelable object. Only used to send data from Android OS to statsd.
 class StatsLogEventWrapper : public android::Parcelable {
  public:
@@ -99,7 +104,9 @@
 
   int64_t getWallClockTimeNs() const { return mWallClockTimeNs; }
 
-  std::vector<StatsLogValue> getElements() const { return mElements; }
+  const std::vector<StatsLogValue>& getElements() const { return mElements; }
+
+  const std::vector<WorkChain>& getWorkChains() const { return mWorkChains; }
 
  private:
   int mTagId;
@@ -109,6 +116,8 @@
   int64_t mWallClockTimeNs;
 
   std::vector<StatsLogValue> mElements;
+
+  std::vector<WorkChain> mWorkChains;
 };
 } // Namespace os
 } // Namespace android
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..f6dfdef 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -58,6 +58,31 @@
     ALOGE("statsd could not read wall clock time from parcel");
     return res;
   }
+  int numWorkChain = 0;
+  if ((res = in->readInt32(&numWorkChain)) != OK) {
+    ALOGE("statsd could not read number of work chains from parcel");
+    return res;
+  }
+  if (numWorkChain > 0) {
+    for (int i = 0; i < numWorkChain; i++) {
+      int numNodes = 0;
+      if ((res = in->readInt32(&numNodes)) != OK) {
+        ALOGE(
+            "statsd could not read number of nodes in work chain from parcel");
+        return res;
+      }
+      if (numNodes == 0) {
+        ALOGE("empty work chain");
+        return BAD_VALUE;
+      }
+      WorkChain wc;
+      for (int j = 0; j < numNodes; j++) {
+        wc.uids.push_back(in->readInt32());
+        wc.tags.push_back(std::string(String8(in->readString16()).string()));
+      }
+      mWorkChains.push_back(wc);
+    }
+  }
   int dataSize = 0;
   if ((res = in->readInt32(&dataSize)) != OK) {
     ALOGE("statsd could not read data size from parcel");
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ff2fad4..ae87998 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -121,18 +121,4 @@
 
     // used by gts tests to verify throttling whitelist
     String[] getBackgroundThrottlingWhitelist();
-
-    /**
-     * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
-     * provider} to start the UI to modify the location permission for a package.
-     *
-     * <p>Can only be called by the location provider.
-     *
-     * @param packageName The package the permission belongs to
-     * @param permission The (individual) permission to switch
-     *
-     * @return A pending intent that starts the permission management UI or {@code null} if the
-     *         intent cannot be created
-     */
-    PendingIntent createManageLocationPermissionIntent(in String packageName, in String permission);
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index b66ceef..3bf98b3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,7 +22,6 @@
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
 import android.Manifest;
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -2401,29 +2400,4 @@
             return null;
         }
     }
-
-    /**
-     * Allow the {@link android.location.LocationManager#getNetworkProviderPackage location
-     * provider} to start the UI to modify the location permission for a package.
-     *
-     * <p>Can only be called by the location provider.
-     *
-     * @param packageName The package the permission belongs to
-     * @param permission The (individual) location permission to switch
-     *
-     * @return A one-shot pending intent that starts the permission management UI or {@code null} if
-     *         the intent cannot be created
-     *
-     * @hide
-     */
-    @SystemApi
-    public @Nullable PendingIntent createManageLocationPermissionIntent(@NonNull String packageName,
-            @NonNull String permission) {
-        try {
-            return mService.createManageLocationPermissionIntent(packageName, permission);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return null;
-        }
-    }
 }
diff --git a/media/OWNERS b/media/OWNERS
index 0abf9ae..03b751c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,12 +1,13 @@
+chz@google.com
+dwkang@google.com
 elaurent@google.com
 etalvala@google.com
 gkasten@google.com
 hunga@google.com
+jaewan@google.com
 jmtrivi@google.com
+jsharkey@android.com
 lajos@google.com
 marcone@google.com
 sungsoo@google.com
 wjia@google.com
-jaewan@google.com
-chz@google.com
-dwkang@google.com
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ff1bdd4..d10900e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -541,6 +541,7 @@
      * Adjusting the volume due to a hardware key press.
      * @hide
      */
+    @SystemApi
     public static final int FLAG_FROM_KEY = 1 << 12;
 
     private static final String[] FLAG_NAMES = {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 2a575b6..4b2353c 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -16,16 +16,6 @@
 
 package android.media;
 
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.ArrayList;
-import java.util.List;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -39,13 +29,21 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
 /**
  * The AudioRecord class manages the audio resources for Java applications
  * to record audio from the audio input hardware of the platform. This is
@@ -1807,6 +1805,8 @@
     private native final int native_get_active_microphones(
             ArrayList<MicrophoneInfo> activeMicrophones);
 
+    private native int native_getPortId();
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 67cc456..082a375 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -75,6 +75,12 @@
      */
     public static final int NUM_STREAMS = 5;
 
+    /** Maximum value for AudioTrack channel count
+     * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+     */
+    public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8();
+    private static native int native_get_FCC_8();
+
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 11;
     @UnsupportedAppUsage
@@ -918,6 +924,15 @@
 
     public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
 
+    /**
+     * Communicate UID of active assistant to audio policy service.
+     */
+    public static native int setAssistantUid(int uid);
+    /**
+     * Communicate UIDs of active accessibility services to audio policy service.
+     */
+    public static native int setA11yServicesUids(int[] uids);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d37f8ab..2c4ec3a 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -93,11 +93,6 @@
      */
     private static final float GAIN_MAX = 1.0f;
 
-    /** Maximum value for AudioTrack channel count
-     * @hide public for MediaCode only, do not un-hide or change to a numeric literal
-     */
-    public static final int CHANNEL_COUNT_MAX = native_get_FCC_8();
-
     /** indicates AudioTrack state is stopped */
     public static final int PLAYSTATE_STOPPED = 1;  // matches SL_PLAYSTATE_STOPPED
     /** indicates AudioTrack state is paused */
@@ -1001,7 +996,8 @@
     }
 
     // mask of all the positional channels supported, however the allowed combinations
-    // are further restricted by the matching left/right rule and CHANNEL_COUNT_MAX
+    // are further restricted by the matching left/right rule and
+    // AudioSystem.OUT_CHANNEL_COUNT_MAX
     private static final int SUPPORTED_OUT_CHANNELS =
             AudioFormat.CHANNEL_OUT_FRONT_LEFT |
             AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
@@ -1124,7 +1120,7 @@
         mChannelIndexMask = channelIndexMask;
         if (mChannelIndexMask != 0) {
             // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
-            final int indexMask = (1 << CHANNEL_COUNT_MAX) - 1;
+            final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
             if ((channelIndexMask & ~indexMask) != 0) {
                 throw new IllegalArgumentException("Unsupported channel index configuration "
                         + channelIndexMask);
@@ -1169,9 +1165,9 @@
             return false;
         }
         final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
-        if (channelCount > CHANNEL_COUNT_MAX) {
+        if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
             loge("Channel configuration contains too many channels " +
-                    channelCount + ">" + CHANNEL_COUNT_MAX);
+                    channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
             return false;
         }
         // check for unsupported multichannel combinations:
@@ -2624,7 +2620,8 @@
      *         to the audio sink.
      *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
      *     queuing as much audio data for playback as possible without blocking.
-     * @param timestamp The timestamp of the first decodable audio frame in the provided audioData.
+     * @param timestamp The timestamp, in nanoseconds, of the first decodable audio frame in the
+     *     provided audioData.
      * @return zero or the positive number of bytes that were written, or one of the following
      *    error codes.
      * <ul>
@@ -3418,7 +3415,6 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback();
     private native final void native_disableDeviceCallback();
-    static private native int native_get_FCC_8();
 
     private native int native_applyVolumeShaper(
             @NonNull VolumeShaper.Configuration configuration,
@@ -3427,6 +3423,8 @@
     private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
     private native final int native_setPresentation(int presentationId, int programId);
 
+    private native int native_getPortId();
+
     //---------------------------------------------------------
     // Utility methods
     //------------------
diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java
index a7e168f..0e8e6ce 100644
--- a/media/java/android/media/CallbackDataSourceDesc.java
+++ b/media/java/android/media/CallbackDataSourceDesc.java
@@ -18,31 +18,31 @@
 
 import android.annotation.NonNull;
 
-import com.android.internal.util.Preconditions;
-
 /**
  * @hide
- * Structure for file data source descriptor.
+ * Structure of data source descriptor for sources using callback.
  *
- * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}.
  *
  */
 public class CallbackDataSourceDesc extends DataSourceDesc {
-    private Media2DataSource mMedia2DataSource;
+    private DataSourceCallback mDataSourceCallback;
 
     private CallbackDataSourceDesc() {
     }
 
     /**
-     * Return the Media2DataSource of this data source.
+     * Return the DataSourceCallback of this data source.
      * It's meaningful only when {@code getType} returns {@link #TYPE_CALLBACK}.
-     * @return the Media2DataSource of this data source
+     * @return the DataSourceCallback of this data source
      */
-    public Media2DataSource getMedia2DataSource() {
-        return mMedia2DataSource;
+    public DataSourceCallback getDataSourceCallback() {
+        return mDataSourceCallback;
     }
 
     /**
@@ -60,7 +60,7 @@
      * </pre>
      */
     public static class Builder extends BuilderBase<Builder> {
-        private Media2DataSource mMedia2DataSource;
+        private DataSourceCallback mDataSourceCallback;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -79,7 +79,7 @@
             if (dsd == null) {
                 return;  // use default
             }
-            mMedia2DataSource = dsd.mMedia2DataSource;
+            mDataSourceCallback = dsd.mDataSourceCallback;
         }
 
         /**
@@ -92,21 +92,21 @@
         public @NonNull CallbackDataSourceDesc build() {
             CallbackDataSourceDesc dsd = new CallbackDataSourceDesc();
             super.build(dsd);
-            dsd.mMedia2DataSource = mMedia2DataSource;
+            dsd.mDataSourceCallback = mDataSourceCallback;
 
             return dsd;
         }
 
         /**
-         * Sets the data source (Media2DataSource) to use.
+         * Sets the data source (DataSourceCallback) to use.
          *
-         * @param m2ds the Media2DataSource for the media to play
+         * @param dscb the DataSourceCallback for the media to play
          * @return the same Builder instance.
-         * @throws NullPointerException if m2ds is null.
+         * @throws NullPointerException if dscb is null.
          */
-        public @NonNull Builder setDataSource(@NonNull Media2DataSource m2ds) {
-            Preconditions.checkNotNull(m2ds);
-            mMedia2DataSource = m2ds;
+        public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) {
+            Media2Utils.checkArgument(dscb != null, "data source cannot be null.");
+            mDataSourceCallback = dscb;
             return this;
         }
     }
diff --git a/media/java/android/media/CloseGuard.java b/media/java/android/media/CloseGuard.java
new file mode 100644
index 0000000..2014673
--- /dev/null
+++ b/media/java/android/media/CloseGuard.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2010 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.media;
+
+import android.util.Log;
+
+/**
+ * Note: This file is copied from dalvik.system package with the following modifications:
+ *       - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
+ *       - Replace System.logW() with android.util.Log.w().
+ *       This file should be used only within media mainline module.
+ * TODO: Remove this file and use dalvik.system.CloseGuard once
+ *       @CorePlatformApi becomes stable or we have a replacement in SDK API.
+ *       b/120419300
+ *
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre>   {@code
+ *   class Foo {
+ *
+ *       {@literal @}ReachabilitySensitive
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Foo() {
+ *           ...;
+ *           guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre>   {@code
+ *   class Bar {
+ *
+ *       {@literal @}ReachabilitySensitive
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Bar() {
+ *           ...;
+ *       }
+ *
+ *       public void connect() {
+ *          ...;
+ *          guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ *
+ * The @ReachabilitySensitive annotation ensures that finalize() cannot be
+ * called during the explicit call to cleanup(), prior to the guard.close call.
+ * There is an extremely small chance that, for code that neglects to call
+ * cleanup(), finalize() and thus cleanup() will be called while a method on
+ * the object is still active, but the "this" reference is no longer required.
+ * If missing cleanup() calls are expected, additional @ReachabilitySensitive
+ * annotations or reachabilityFence() calls may be required.
+ *
+ * @hide
+ */
+final class CloseGuard {
+
+    /**
+     * True if collection of call-site information (the expensive operation
+     * here)  and tracking via a Tracker (see below) are enabled.
+     * Enabled by default so we can diagnose issues early in VM startup.
+     * Note, however, that Android disables this early in its startup,
+     * but enables it with DropBoxing for system apps on debug builds.
+     */
+    private static volatile boolean stackAndTrackingEnabled = true;
+
+    /**
+     * Hook for customizing how CloseGuard issues are reported.
+     * Bypassed if stackAndTrackingEnabled was false when open was called.
+     */
+    private static volatile Reporter reporter = new DefaultReporter();
+
+    /**
+     * Hook for customizing how CloseGuard issues are tracked.
+     */
+    private static volatile Tracker currentTracker = null; // Disabled by default.
+
+    /**
+     * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+     * up the instance to warn on failure to close.
+     */
+    public static CloseGuard get() {
+        return new CloseGuard();
+    }
+
+    /**
+     * Enables/disables stack capture and tracking. A call stack is captured
+     * during open(), and open/close events are reported to the Tracker, only
+     * if enabled is true. If a stack trace was captured, the {@link
+     * #getReporter() reporter} is informed of unclosed resources; otherwise a
+     * one-line warning is logged.
+     */
+    public static void setEnabled(boolean enabled) {
+        CloseGuard.stackAndTrackingEnabled = enabled;
+    }
+
+    /**
+     * True if CloseGuard stack capture and tracking are enabled.
+     */
+    public static boolean isEnabled() {
+        return stackAndTrackingEnabled;
+    }
+
+    /**
+     * Used to replace default Reporter used to warn of CloseGuard
+     * violations when stack tracking is enabled. Must be non-null.
+     */
+    public static void setReporter(Reporter rep) {
+        if (rep == null) {
+            throw new NullPointerException("reporter == null");
+        }
+        CloseGuard.reporter = rep;
+    }
+
+    /**
+     * Returns non-null CloseGuard.Reporter.
+     */
+    public static Reporter getReporter() {
+        return reporter;
+    }
+
+    /**
+     * Sets the {@link Tracker} that is notified when resources are allocated and released.
+     * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+     * was called. A null argument disables tracking.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public static void setTracker(Tracker tracker) {
+        currentTracker = tracker;
+    }
+
+    /**
+     * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+     * there is none.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public static Tracker getTracker() {
+        return currentTracker;
+    }
+
+    private CloseGuard() {}
+
+    /**
+     * {@code open} initializes the instance with a warning that the caller
+     * should have explicitly called the {@code closer} method instead of
+     * relying on finalization.
+     *
+     * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+     * @throws NullPointerException if closer is null.
+     */
+    public void open(String closer) {
+        // always perform the check for valid API usage...
+        if (closer == null) {
+            throw new NullPointerException("closer == null");
+        }
+        // ...but avoid allocating an allocation stack if "disabled"
+        if (!stackAndTrackingEnabled) {
+            closerNameOrAllocationInfo = closer;
+            return;
+        }
+        String message = "Explicit termination method '" + closer + "' not called";
+        Throwable stack = new Throwable(message);
+        closerNameOrAllocationInfo = stack;
+        Tracker tracker = currentTracker;
+        if (tracker != null) {
+            tracker.open(stack);
+        }
+    }
+
+    // We keep either an allocation stack containing the closer String or, when
+    // in disabled state, just the closer String.
+    // We keep them in a single field only to minimize overhead.
+    private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+    /**
+     * Marks this CloseGuard instance as closed to avoid warnings on
+     * finalization.
+     */
+    public void close() {
+        Tracker tracker = currentTracker;
+        if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
+            // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
+            tracker.close((Throwable) closerNameOrAllocationInfo);
+        }
+        closerNameOrAllocationInfo = null;
+    }
+
+    /**
+     * Logs a warning if the caller did not properly cleanup by calling an
+     * explicit close method before finalization. If CloseGuard was enabled
+     * when the CloseGuard was created, passes the stacktrace associated with
+     * the allocation to the current reporter. If it was not enabled, it just
+     * directly logs a brief message.
+     */
+    public void warnIfOpen() {
+        if (closerNameOrAllocationInfo != null) {
+            if (closerNameOrAllocationInfo instanceof String) {
+                Log.w("CloseGuard", "A resource failed to call "
+                        + (String) closerNameOrAllocationInfo + ". ");
+            } else {
+                String message =
+                        "A resource was acquired at attached stack trace but never released. ";
+                message += "See java.io.Closeable for information on avoiding resource leaks.";
+                Throwable stack = (Throwable) closerNameOrAllocationInfo;
+                reporter.report(message, stack);
+            }
+        }
+    }
+
+    /**
+     * Interface to allow customization of tracking behaviour.
+     *
+     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+     * MUST NOT be used for any other purposes.
+     */
+    public interface Tracker {
+        void open(Throwable allocationSite);
+        void close(Throwable allocationSite);
+    }
+
+    /**
+     * Interface to allow customization of reporting behavior.
+     * @hide
+     */
+    public interface Reporter {
+        void report(String message, Throwable allocationSite);
+    }
+
+    /**
+     * Default Reporter which reports CloseGuard violations to the log.
+     */
+    private static final class DefaultReporter implements Reporter {
+        private DefaultReporter() {}
+
+        @Override public void report (String message, Throwable allocationSite) {
+            Log.w("CloseGuard", message, allocationSite);
+        }
+    }
+}
diff --git a/media/java/android/media/Media2DataSource.java b/media/java/android/media/DataSourceCallback.java
similarity index 89%
rename from media/java/android/media/Media2DataSource.java
rename to media/java/android/media/DataSourceCallback.java
index 08df632..9b27baf 100644
--- a/media/java/android/media/Media2DataSource.java
+++ b/media/java/android/media/DataSourceCallback.java
@@ -27,12 +27,12 @@
  *
  * <p class="note">Methods of this interface may be called on multiple different
  * threads. There will be a thread synchronization point between each call to ensure that
- * modifications to the state of your Media2DataSource are visible to future calls. This means
+ * modifications to the state of your DataSourceCallback are visible to future calls. This means
  * you don't need to do your own synchronization unless you're modifying the
- * Media2DataSource from another thread while it's being used by the framework.</p>
+ * DataSourceCallback from another thread while it's being used by the framework.</p>
  *
  */
-public abstract class Media2DataSource implements Closeable {
+public abstract class DataSourceCallback implements Closeable {
     /**
      * Called to request data from the given position.
      *
diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java
index aed3f84..702034e 100644
--- a/media/java/android/media/DataSourceDesc.java
+++ b/media/java/android/media/DataSourceDesc.java
@@ -18,13 +18,13 @@
 
 import android.annotation.NonNull;
 
-import com.android.internal.util.Preconditions;
-
 /**
  * @hide
  * Base class of data source descriptor.
  *
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use subclasses' builder to change {@link DataSourceDesc}.
@@ -32,7 +32,7 @@
  */
 public class DataSourceDesc {
     // intentionally less than long.MAX_VALUE
-    public static final long LONG_MAX = 0x7ffffffffffffffL;
+    static final long LONG_MAX = 0x7ffffffffffffffL;
 
     // keep consistent with native code
     public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
@@ -48,6 +48,19 @@
     }
 
     /**
+     * Releases the resources held by this {@code DataSourceDesc} object.
+     */
+    void close() {
+    }
+
+    // Have to declare protected for finalize() since it is protected
+    // in the base class Object.
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+    }
+
+    /**
      * Return the media Id of data source.
      * @return the media Id of data source
      */
@@ -118,7 +131,7 @@
          * @return the same instance of subclass of {@link DataSourceDesc}
          */
         void build(@NonNull DataSourceDesc dsd) {
-            Preconditions.checkNotNull(dsd);
+            Media2Utils.checkArgument(dsd != null,  "dsd cannot be null.");
 
             if (mStartPositionMs > mEndPositionMs) {
                 throw new IllegalStateException("Illegal start/end position: "
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index b96a585..32c4643 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -16,7 +16,10 @@
 
 package android.media;
 
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.Bitmap;
@@ -26,12 +29,14 @@
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.Pair;
-import android.annotation.IntDef;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
 import java.io.DataInput;
+import java.io.DataInputStream;
 import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -42,14 +47,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
-import java.util.LinkedList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -58,11 +63,6 @@
 import java.util.TimeZone;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
 
 /**
  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
@@ -583,11 +583,19 @@
     private static class ExifAttribute {
         public final int format;
         public final int numberOfComponents;
+        public final long bytesOffset;
         public final byte[] bytes;
 
+        public static final long BYTES_OFFSET_UNKNOWN = -1;
+
         private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
+            this(format, numberOfComponents, BYTES_OFFSET_UNKNOWN, bytes);
+        }
+
+        private ExifAttribute(int format, int numberOfComponents, long bytesOffset, byte[] bytes) {
             this.format = format;
             this.numberOfComponents = numberOfComponents;
+            this.bytesOffset = bytesOffset;
             this.bytes = bytes;
         }
 
@@ -1318,6 +1326,7 @@
     private int mOrfThumbnailLength;
     private int mRw2JpgFromRawOffset;
     private boolean mIsSupportedFile;
+    private boolean mModified;
 
     // Pattern to check non zero timestamp
     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1328,7 +1337,14 @@
     /**
      * Reads Exif tags from the specified image file.
      */
-    public ExifInterface(String filename) throws IOException {
+    public ExifInterface(@NonNull File file) throws IOException {
+        this(file.getAbsolutePath());
+    }
+
+    /**
+     * Reads Exif tags from the specified image file.
+     */
+    public ExifInterface(@NonNull String filename) throws IOException {
         if (filename == null) {
             throw new IllegalArgumentException("filename cannot be null");
         }
@@ -1354,7 +1370,7 @@
      * for writable and seekable file descriptors only. This constructor will not rewind the offset
      * of the given file descriptor. Developers should close the file descriptor after use.
      */
-    public ExifInterface(FileDescriptor fileDescriptor) throws IOException {
+    public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException {
         if (fileDescriptor == null) {
             throw new IllegalArgumentException("fileDescriptor cannot be null");
         }
@@ -1388,7 +1404,7 @@
      * for input streams. The given input stream will proceed its current position. Developers
      * should close the input stream after use.
      */
-    public ExifInterface(InputStream inputStream) throws IOException {
+    public ExifInterface(@NonNull InputStream inputStream) throws IOException {
         if (inputStream == null) {
             throw new IllegalArgumentException("inputStream cannot be null");
         }
@@ -1414,7 +1430,7 @@
      *
      * @param tag the name of the tag.
      */
-    private ExifAttribute getExifAttribute(String tag) {
+    private @Nullable ExifAttribute getExifAttribute(@NonNull String tag) {
         // Retrieves all tag groups. The value from primary image tag group has a higher priority
         // than the value from the thumbnail tag group if there are more than one candidates.
         for (int i = 0; i < EXIF_TAGS.length; ++i) {
@@ -1432,7 +1448,7 @@
      *
      * @param tag the name of the tag.
      */
-    public String getAttribute(String tag) {
+    public @Nullable String getAttribute(@NonNull String tag) {
         ExifAttribute attribute = getExifAttribute(tag);
         if (attribute != null) {
             if (!sTagSetForCompatibility.contains(tag)) {
@@ -1470,7 +1486,7 @@
      * @param tag the name of the tag.
      * @param defaultValue the value to return if the tag is not available.
      */
-    public int getAttributeInt(String tag, int defaultValue) {
+    public int getAttributeInt(@NonNull String tag, int defaultValue) {
         ExifAttribute exifAttribute = getExifAttribute(tag);
         if (exifAttribute == null) {
             return defaultValue;
@@ -1491,7 +1507,7 @@
      * @param tag the name of the tag.
      * @param defaultValue the value to return if the tag is not available.
      */
-    public double getAttributeDouble(String tag, double defaultValue) {
+    public double getAttributeDouble(@NonNull String tag, double defaultValue) {
         ExifAttribute exifAttribute = getExifAttribute(tag);
         if (exifAttribute == null) {
             return defaultValue;
@@ -1510,7 +1526,7 @@
      * @param tag the name of the tag.
      * @param value the value of the tag.
      */
-    public void setAttribute(String tag, String value) {
+    public void setAttribute(@NonNull String tag, @Nullable String value) {
         // Convert the given value to rational values for backwards compatibility.
         if (value != null && sTagSetForCompatibility.contains(tag)) {
             if (tag.equals(TAG_GPS_TIMESTAMP)) {
@@ -1772,12 +1788,18 @@
     }
 
     /**
-     * Save the tag data into the original image file. This is expensive because it involves
-     * copying all the data from one file to another and deleting the old file and renaming the
-     * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
-     * and make a single call rather than multiple calls for each attribute.
+     * Save the tag data into the original image file. This is expensive because
+     * it involves copying all the data from one file to another and deleting
+     * the old file and renaming the other. It's best to use
+     * {@link #setAttribute(String,String)} to set all attributes to write and
+     * make a single call rather than multiple calls for each attribute.
      * <p>
      * This method is only supported for JPEG files.
+     * <p class="note">
+     * Note: after calling this method, any attempts to obtain range information
+     * from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
+     * will throw {@link IllegalStateException}, since the offsets may have
+     * changed in the newly written file.
      * </p>
      */
     public void saveAttributes() throws IOException {
@@ -1789,6 +1811,10 @@
                     "ExifInterface does not support saving attributes for the current input.");
         }
 
+        // Remember the fact that we've changed the file on disk from what was
+        // originally parsed, meaning we can't answer range questions
+        mModified = true;
+
         // Keep the thumbnail in memory
         mThumbnailBytes = getThumbnail();
 
@@ -1849,6 +1875,15 @@
     }
 
     /**
+     * Returns true if the image file has the given attribute defined.
+     *
+     * @param tag the name of the tag.
+     */
+    public boolean hasAttribute(String tag) {
+        return (getExifAttribute(tag) != null);
+    }
+
+    /**
      * Returns the JPEG compressed thumbnail inside the image file, or {@code null} if there is no
      * JPEG compressed thumbnail.
      * The returned data can be decoded using
@@ -1968,17 +2003,45 @@
      *
      * @return two-element array, the offset in the first value, and length in
      *         the second, or {@code null} if no thumbnail was found.
+     * @throws IllegalStateException if {@link #saveAttributes()} has been
+     *             called since the underlying file was initially parsed, since
+     *             that means offsets may have changed.
      */
-    public long[] getThumbnailRange() {
-        if (!mHasThumbnail) {
-            return null;
+    public @Nullable long[] getThumbnailRange() {
+        if (mModified) {
+            throw new IllegalStateException(
+                    "The underlying file has been modified since being parsed");
         }
 
-        long[] range = new long[2];
-        range[0] = mThumbnailOffset;
-        range[1] = mThumbnailLength;
+        if (mHasThumbnail) {
+            return new long[] { mThumbnailOffset, mThumbnailLength };
+        } else {
+            return null;
+        }
+    }
 
-        return range;
+    /**
+     * Returns the offset and length of the requested tag inside the image file,
+     * or {@code null} if the tag is not contained.
+     *
+     * @return two-element array, the offset in the first value, and length in
+     *         the second, or {@code null} if no tag was found.
+     * @throws IllegalStateException if {@link #saveAttributes()} has been
+     *             called since the underlying file was initially parsed, since
+     *             that means offsets may have changed.
+     */
+    public @Nullable long[] getAttributeRange(@NonNull String tag) {
+        if (mModified) {
+            throw new IllegalStateException(
+                    "The underlying file has been modified since being parsed");
+        }
+
+        final ExifAttribute attribute = getExifAttribute(tag);
+        if (attribute != null) {
+            return new long[] { attribute.bytesOffset, attribute.bytes.length };
+        } else {
+            return null;
+        }
     }
 
     /**
@@ -2023,13 +2086,41 @@
     }
 
     /**
-     * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
-     * Returns -1 if the date time information if not available.
+     * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
+     * 
      * @hide
      */
     @UnsupportedAppUsage
-    public long getDateTime() {
-        String dateTimeString = getAttribute(TAG_DATETIME);
+    public @CurrentTimeMillisLong long getDateTime() {
+        return parseDateTime(getAttribute(TAG_DATETIME),
+                getAttribute(TAG_SUBSEC_TIME));
+    }
+
+    /**
+     * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
+     * invalid.
+     *
+     * @hide
+     */
+    public @CurrentTimeMillisLong long getDateTimeDigitized() {
+        return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
+                getAttribute(TAG_SUBSEC_TIME_DIGITIZED));
+    }
+
+    /**
+     * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
+     * invalid.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public @CurrentTimeMillisLong long getDateTimeOriginal() {
+        return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
+                getAttribute(TAG_SUBSEC_TIME_ORIGINAL));
+    }
+
+    private static @CurrentTimeMillisLong long parseDateTime(@Nullable String dateTimeString,
+            @Nullable String subSecs) {
         if (dateTimeString == null
                 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
 
@@ -2041,7 +2132,6 @@
             if (datetime == null) return -1;
             long msecs = datetime.getTime();
 
-            String subSecs = getAttribute(TAG_SUBSEC_TIME);
             if (subSecs != null) {
                 try {
                     long sub = Long.parseLong(subSecs);
@@ -2545,13 +2635,18 @@
                     if (size == 0) {
                         return 0;
                     }
-                    // We don't allow read positions after the available bytes,
-                    // the input stream won't be able to seek back then.
-                    if (position < 0 || position >= in.available()) {
+                    if (position < 0) {
                         return -1;
                     }
                     try {
                         if (mPosition != position) {
+                            // We don't allow seek to positions after the available bytes,
+                            // the input stream won't be able to seek back then.
+                            // However, if we hit an exception before (mPosition set to -1),
+                            // let it try the seek in hope it might recover.
+                            if (mPosition >= 0 && position >= mPosition + in.available()) {
+                                return -1;
+                            }
                             in.seek(position);
                             mPosition = position;
                         }
@@ -2559,8 +2654,8 @@
                         // If the read will cause us to go over the available bytes,
                         // reduce the size so that we stay in the available range.
                         // Otherwise the input stream may not be able to seek back.
-                        if (mPosition + size > in.available()) {
-                            size = in.available() - (int)mPosition;
+                        if (size > in.available()) {
+                            size = in.available();
                         }
 
                         int bytesRead = in.read(buffer, offset, size);
@@ -3125,9 +3220,11 @@
                 continue;
             }
 
-            byte[] bytes = new byte[(int) byteCount];
+            final int bytesOffset = dataInputStream.peek();
+            final byte[] bytes = new byte[(int) byteCount];
             dataInputStream.readFully(bytes);
-            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, bytes);
+            ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents,
+                    bytesOffset, bytes);
             mAttributes[ifdType].put(tag.name, attribute);
 
             // DNG files have a DNG Version tag specifying the version of specifications that the
diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java
index 5d8ff49..763a81f 100644
--- a/media/java/android/media/FileDataSourceDesc.java
+++ b/media/java/android/media/FileDataSourceDesc.java
@@ -17,22 +17,26 @@
 package android.media;
 
 import android.annotation.NonNull;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
+import java.io.IOException;
 
 /**
  * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using file descriptor.
  *
- * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
  *
  */
 public class FileDataSourceDesc extends DataSourceDesc {
+    private static final String TAG = "FileDataSourceDesc";
+
     /**
      * Used when the length of file descriptor is unknown.
      *
@@ -40,34 +44,61 @@
      */
     public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
 
-    private FileDescriptor mFD;
+    private ParcelFileDescriptor mPFD;
     private long mOffset = 0;
     private long mLength = FD_LENGTH_UNKNOWN;
 
     private FileDataSourceDesc() {
+        super();
     }
 
     /**
-     * Return the FileDescriptor of this data source.
-     * @return the FileDescriptor of this data source
+     * Releases the resources held by this {@code FileDataSourceDesc} object.
      */
-    public FileDescriptor getFileDescriptor() {
-        return mFD;
+    @Override
+    void close() {
+        super.close();
+        closeFD();
     }
 
     /**
-     * Return the offset associated with the FileDescriptor of this data source.
+     * Releases the file descriptor held by this {@code FileDataSourceDesc} object.
+     */
+    void closeFD() {
+        synchronized (this) {
+            if (mPFD != null) {
+                try {
+                    mPFD.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "failed to close pfd: " + e);
+                }
+
+                mPFD = null;
+            }
+        }
+    }
+
+    /**
+     * Return the ParcelFileDescriptor of this data source.
+     * @return the ParcelFileDescriptor of this data source
+     */
+    public ParcelFileDescriptor getParcelFileDescriptor() {
+        return mPFD;
+    }
+
+    /**
+     * Return the offset associated with the ParcelFileDescriptor of this data source.
      * It's meaningful only when it has been set by the {@link Builder}.
-     * @return the offset associated with the FileDescriptor of this data source
+     * @return the offset associated with the ParcelFileDescriptor of this data source
      */
     public long getOffset() {
         return mOffset;
     }
 
     /**
-     * Return the content length associated with the FileDescriptor of this data source.
+     * Return the content length associated with the ParcelFileDescriptor of this data source.
      * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
-     * @return the content length associated with the FileDescriptor of this data source
+     * @return the content length associated with the ParcelFileDescriptor of this data source
      */
     public long getLength() {
         return mLength;
@@ -80,7 +111,7 @@
      *
      * <pre class="prettyprint">
      * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
-     *         .setDataSource(fd, 0, srcLength)
+     *         .setDataSource(pfd, 0, srcLength)
      *         .setStartPosition(1000)
      *         .setEndPosition(15000)
      *         .build();
@@ -88,7 +119,7 @@
      * </pre>
      */
     public static class Builder extends BuilderBase<Builder> {
-        private FileDescriptor mFD;
+        private ParcelFileDescriptor mPFD;
         private long mOffset = 0;
         private long mLength = FD_LENGTH_UNKNOWN;
 
@@ -109,7 +140,7 @@
             if (dsd == null) {
                 return;  // use default
             }
-            mFD = dsd.mFD;
+            mPFD = dsd.mPFD;
             mOffset = dsd.mOffset;
             mLength = dsd.mLength;
         }
@@ -124,7 +155,7 @@
         public @NonNull FileDataSourceDesc build() {
             FileDataSourceDesc dsd = new FileDataSourceDesc();
             super.build(dsd);
-            dsd.mFD = mFD;
+            dsd.mPFD = mPFD;
             dsd.mOffset = mOffset;
             dsd.mLength = mLength;
 
@@ -132,38 +163,46 @@
         }
 
         /**
-         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
-         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
-         * to close the file descriptor after the source has been used.
+         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+         * created by this builder is passed to {@link MediaPlayer2} via
+         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+         * close the ParcelFileDescriptor.
          *
-         * @param fd the FileDescriptor for the file to play
+         * @param pfd the ParcelFileDescriptor for the file to play
          * @return the same Builder instance.
-         * @throws NullPointerException if fd is null.
+         * @throws NullPointerException if pfd is null.
          */
-        public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) {
-            Preconditions.checkNotNull(fd);
+        public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) {
+            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
             resetDataSource();
-            mFD = fd;
+            mPFD = pfd;
             return this;
         }
 
         /**
-         * Sets the data source (FileDescriptor) to use. The FileDescriptor must be
-         * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
-         * to close the file descriptor after the source has been used.
+         * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be
+         * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc}
+         * created by this builder is passed to {@link MediaPlayer2} via
+         * {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+         * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or
+         * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will
+         * close the ParcelFileDescriptor.
          *
          * Any negative number for offset is treated as 0.
          * Any negative number for length is treated as maximum length of the data source.
          *
-         * @param fd the FileDescriptor for the file to play
+         * @param pfd the ParcelFileDescriptor for the file to play
          * @param offset the offset into the file where the data to be played starts, in bytes
          * @param length the length in bytes of the data to be played
          * @return the same Builder instance.
-         * @throws NullPointerException if fd is null.
+         * @throws NullPointerException if pfd is null.
          */
         public @NonNull Builder setDataSource(
-                @NonNull FileDescriptor fd, long offset, long length) {
-            Preconditions.checkNotNull(fd);
+                @NonNull ParcelFileDescriptor pfd, long offset, long length) {
+            Media2Utils.checkArgument(pfd != null, "pfd cannot be null.");
             if (offset < 0) {
                 offset = 0;
             }
@@ -171,14 +210,14 @@
                 length = FD_LENGTH_UNKNOWN;
             }
             resetDataSource();
-            mFD = fd;
+            mPFD = pfd;
             mOffset = offset;
             mLength = length;
             return this;
         }
 
         private void resetDataSource() {
-            mFD = null;
+            mPFD = null;
             mOffset = 0;
             mLength = FD_LENGTH_UNKNOWN;
         }
diff --git a/media/java/android/media/Media2HTTPConnection.java b/media/java/android/media/Media2HTTPConnection.java
index 0d7825a..a369a62 100644
--- a/media/java/android/media/Media2HTTPConnection.java
+++ b/media/java/android/media/Media2HTTPConnection.java
@@ -16,27 +16,27 @@
 
 package android.media;
 
-import android.net.NetworkUtils;
+import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
 import android.os.StrictMode;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
 import java.net.HttpURLConnection;
+import java.net.InetAddress;
 import java.net.MalformedURLException;
 import java.net.NoRouteToHostException;
 import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.UnknownHostException;
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
 
-import static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
-
 /** @hide */
 public class Media2HTTPConnection {
     private static final String TAG = "Media2HTTPConnection";
@@ -161,10 +161,10 @@
             if (host.equalsIgnoreCase("localhost")) {
                 return true;
             }
-            if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
+            if (InetAddress.getByName(host).isLoopbackAddress()) {
                 return true;
             }
-        } catch (IllegalArgumentException iex) {
+        } catch (IllegalArgumentException | UnknownHostException e) {
         }
         return false;
     }
diff --git a/media/java/android/media/Media2Utils.java b/media/java/android/media/Media2Utils.java
index 066233d..5fd6191 100644
--- a/media/java/android/media/Media2Utils.java
+++ b/media/java/android/media/Media2Utils.java
@@ -31,6 +31,20 @@
     private Media2Utils() {
     }
 
+    /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
+    public static void checkArgument(boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+    }
+
     public static synchronized void storeCookies(List<HttpCookie> cookies) {
         CookieHandler cookieHandler = CookieHandler.getDefault();
         if (cookieHandler == null) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index dfe29e9..6301993 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import static android.media.Utils.intersectSortedDistinctRanges;
+import static android.media.Utils.sortDistinctRanges;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
@@ -32,9 +35,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.media.Utils.intersectSortedDistinctRanges;
-import static android.media.Utils.sortDistinctRanges;
-
 /**
  * Provides information about a given media codec available on the device. You can
  * iterate through all codecs available by querying {@link MediaCodecList}. For example,
@@ -1117,7 +1117,7 @@
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_RAW)) {
                 sampleRateRange = Range.create(1, 96000);
                 bitRates = Range.create(1, 10000000);
-                maxChannels = AudioTrack.CHANNEL_COUNT_MAX;
+                maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX;
             } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_AUDIO_FLAC)) {
                 sampleRateRange = Range.create(1, 655350);
                 // lossless codec, so bitrate is ignored
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 284e422..b7743c9 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -17,20 +17,29 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.AbstractSet;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
- * Encapsulates the information describing the format of media data,
- * be it audio or video.
- *
- * The format of the media data is specified as string/value pairs.
- *
+ * Encapsulates the information describing the format of media data, be it audio or video, as
+ * well as optional feature metadata.
+ * <p>
+ * The format of the media data is specified as key/value pairs. Keys are strings. Values can
+ * be integer, long, float, String or ByteBuffer.
+ * <p>
+ * The feature metadata is specificed as string/boolean pairs.
+ * <p>
  * Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:
  *
  * <table>
@@ -938,7 +947,6 @@
      */
     public static final String KEY_CA_SESSION_ID = "ca-session-id";
 
-
     /**
      * A key describing the private data in the CA_descriptor associated with a media track.
      * <p>
@@ -950,7 +958,7 @@
      */
     public static final String KEY_CA_PRIVATE_DATA = "ca-private-data";
 
-    /* package private */ MediaFormat(Map<String, Object> map) {
+    /* package private */ MediaFormat(@NonNull Map<String, Object> map) {
         mMap = map;
     }
 
@@ -969,11 +977,58 @@
     /**
      * Returns true iff a key of the given name exists in the format.
      */
-    public final boolean containsKey(String name) {
+    public final boolean containsKey(@NonNull String name) {
         return mMap.containsKey(name);
     }
 
     /**
+     * Returns true iff a feature of the given name exists in the format.
+     */
+    public final boolean containsFeature(@NonNull String name) {
+        return mMap.containsKey(KEY_FEATURE_ + name);
+    }
+
+    public static final int TYPE_NULL = 0;
+    public static final int TYPE_INTEGER = 1;
+    public static final int TYPE_LONG = 2;
+    public static final int TYPE_FLOAT = 3;
+    public static final int TYPE_STRING = 4;
+    public static final int TYPE_BYTE_BUFFER = 5;
+
+    /** @hide */
+    @IntDef({
+        TYPE_NULL,
+        TYPE_INTEGER,
+        TYPE_LONG,
+        TYPE_FLOAT,
+        TYPE_STRING,
+        TYPE_BYTE_BUFFER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    /**
+     * Returns the value type for a key. If the key does not exist, it returns TYPE_NULL.
+     */
+    public final @Type int getValueTypeForKey(@NonNull String name) {
+        Object value = mMap.get(name);
+        if (value == null) {
+            return TYPE_NULL;
+        } else if (value instanceof Integer) {
+            return TYPE_INTEGER;
+        } else if (value instanceof Long) {
+            return TYPE_LONG;
+        } else if (value instanceof Float) {
+            return TYPE_FLOAT;
+        } else if (value instanceof String) {
+            return TYPE_STRING;
+        } else if (value instanceof ByteBuffer) {
+            return TYPE_BYTE_BUFFER;
+        }
+        throw new RuntimeException("invalid value for key");
+    }
+
+    /**
      * A key prefix used together with a {@link MediaCodecInfo.CodecCapabilities}
      * feature name describing a required or optional feature for a codec capabilities
      * query.
@@ -989,64 +1044,165 @@
     public static final String KEY_FEATURE_ = "feature-";
 
     /**
-     * Returns the value of an integer key.
+     * Returns the value of a numeric key. This is provided as a convenience method for keys
+     * that may take multiple numeric types, such as {@link #KEY_FRAME_RATE}, or {@link
+     * #KEY_I_FRAME_INTERVAL}.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is ByteBuffer or String
      */
-    public final int getInteger(String name) {
+    public final @Nullable Number getNumber(@NonNull String name) {
+        return ((Number)mMap.get(name));
+    }
+
+    /**
+     * Returns the value of a numeric key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is ByteBuffer or String
+     */
+    public final @NonNull Number getNumber(@NonNull String name, @NonNull Number defaultValue) {
+        Number ret = getNumber(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
+     * Returns the value of an integer key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+     *         String
+     */
+    public final int getInteger(@NonNull String name) {
         return ((Integer)mMap.get(name)).intValue();
     }
 
     /**
-     * Returns the value of an integer key, or the default value if the
-     * key is missing or is for another type value.
-     * @hide
+     * Returns the value of an integer key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is long, float, ByteBuffer or
+     *         String
      */
-    public final int getInteger(String name, int defaultValue) {
+    public final int getInteger(@NonNull String name, int defaultValue) {
         try {
             return getInteger(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
         }
-        catch (NullPointerException  e) { /* no such field */ }
-        catch (ClassCastException e) { /* field of different type */ }
-        return defaultValue;
     }
 
     /**
      * Returns the value of a long key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+     *         String
      */
-    public final long getLong(String name) {
+    public final long getLong(@NonNull String name) {
         return ((Long)mMap.get(name)).longValue();
     }
 
     /**
-     * Returns the value of a float key.
+     * Returns the value of an long key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, float, ByteBuffer or
+     *         String
      */
-    public final float getFloat(String name) {
+    public final long getLong(@NonNull String name, long defaultValue) {
+        try {
+            return getLong(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value of a float key.
+     *
+     * @throws NullPointerException if the key does not exist or the stored value for the key is
+     *         null
+     * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+     *         String
+     */
+    public final float getFloat(@NonNull String name) {
         return ((Float)mMap.get(name)).floatValue();
     }
 
     /**
-     * Returns the value of a string key.
+     * Returns the value of an float key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, ByteBuffer or
+     *         String
      */
-    public final String getString(String name) {
+    public final float getFloat(@NonNull String name, float defaultValue) {
+        try {
+            return getFloat(name);
+        } catch (NullPointerException  e) {
+            /* no such field or field is null */
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the value of a string key.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
+     */
+    public final @Nullable String getString(@NonNull String name) {
         return (String)mMap.get(name);
     }
 
     /**
-     * Returns the value of a ByteBuffer key.
+     * Returns the value of an string key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or ByteBuffer
      */
-    public final ByteBuffer getByteBuffer(String name) {
+    public final @NonNull String getString(@NonNull String name, @NonNull String defaultValue) {
+        String ret = getString(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
+     * Returns the value of a ByteBuffer key.
+     *
+     * @return null if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or String
+     */
+    public final @Nullable ByteBuffer getByteBuffer(@NonNull String name) {
         return (ByteBuffer)mMap.get(name);
     }
 
     /**
+     * Returns the value of a ByteBuffer key, or the default value if the key is missing.
+     *
+     * @return defaultValue if the key does not exist or the stored value for the key is null
+     * @throws ClassCastException if the stored value for the key is int, long, float or String
+     */
+    public final @NonNull ByteBuffer getByteBuffer(
+            @NonNull String name, @NonNull ByteBuffer defaultValue) {
+        ByteBuffer ret = getByteBuffer(name);
+        return ret == null ? defaultValue : ret;
+    }
+
+    /**
      * Returns whether a feature is to be enabled ({@code true}) or disabled
      * ({@code false}).
      *
      * @param feature the name of a {@link MediaCodecInfo.CodecCapabilities} feature.
      *
      * @throws IllegalArgumentException if the feature was neither set to be enabled
-     *        nor to be disabled.
+     *         nor to be disabled.
      */
-    public boolean getFeatureEnabled(String feature) {
+    public boolean getFeatureEnabled(@NonNull String feature) {
         Integer enabled = (Integer)mMap.get(KEY_FEATURE_ + feature);
         if (enabled == null) {
             throw new IllegalArgumentException("feature is not specified");
@@ -1057,39 +1213,239 @@
     /**
      * Sets the value of an integer key.
      */
-    public final void setInteger(String name, int value) {
+    public final void setInteger(@NonNull String name, int value) {
         mMap.put(name, Integer.valueOf(value));
     }
 
     /**
      * Sets the value of a long key.
      */
-    public final void setLong(String name, long value) {
+    public final void setLong(@NonNull String name, long value) {
         mMap.put(name, Long.valueOf(value));
     }
 
     /**
      * Sets the value of a float key.
      */
-    public final void setFloat(String name, float value) {
+    public final void setFloat(@NonNull String name, float value) {
         mMap.put(name, new Float(value));
     }
 
     /**
      * Sets the value of a string key.
+     * <p>
+     * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * remove a key.
      */
-    public final void setString(String name, String value) {
+    public final void setString(@NonNull String name, @Nullable String value) {
         mMap.put(name, value);
     }
 
     /**
      * Sets the value of a ByteBuffer key.
+     * <p>
+     * If value is {@code null}, it sets a null value that behaves similarly to a missing key.
+     * This could be used prior to API level {@link android os.Build.VERSION_CODES#Q} to effectively
+     * remove a key.
      */
-    public final void setByteBuffer(String name, ByteBuffer bytes) {
+    public final void setByteBuffer(@NonNull String name, @Nullable ByteBuffer bytes) {
         mMap.put(name, bytes);
     }
 
     /**
+     * Removes a value of a given key if present. Has no effect if the key is not present.
+     */
+    public final void removeKey(@NonNull String name) {
+        // exclude feature mappings
+        if (!name.startsWith(KEY_FEATURE_)) {
+            mMap.remove(name);
+        }
+    }
+
+    /**
+     * Removes a given feature setting if present. Has no effect if the feature setting is not
+     * present.
+     */
+    public final void removeFeature(@NonNull String name) {
+        mMap.remove(KEY_FEATURE_ + name);
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object.
+     *
+     * This class is needed as we want to return a portion of the actual format keys in getKeys()
+     * and another portion of the keys in getFeatures(), and still allow the view properties.
+     */
+    private abstract class FilteredMappedKeySet extends AbstractSet<String> {
+        private Set<String> mKeys;
+
+        // Returns true if this set should include this key
+        abstract protected boolean keepKey(String key);
+
+        // Maps a key from the underlying key set into its new value in this key set
+        abstract protected String mapKeyToItem(String key);
+
+        // Maps a key from this key set into its original value in the underlying key set
+        abstract protected String mapItemToKey(String item);
+
+        public FilteredMappedKeySet() {
+            mKeys = mMap.keySet();
+        }
+
+        // speed up contains and remove from abstract implementation (that would iterate
+        // over each element)
+        @Override
+        public boolean contains(Object o) {
+            if (o instanceof String) {
+                String key = mapItemToKey((String)o);
+                return keepKey(key) && mKeys.contains(key);
+            }
+            return false;
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            if (o instanceof String) {
+                String key = mapItemToKey((String)o);
+                if (keepKey(key) && mKeys.remove(key)) {
+                    mMap.remove(key);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private class KeyIterator implements Iterator<String> {
+            Iterator<String> mIterator;
+            String mLast;
+
+            public KeyIterator() {
+                // We must create a copy of the filtered stream, as remove operation has to modify
+                // the underlying data structure (mMap), so the iterator's operation is undefined.
+                // Use a list as it is likely less memory consuming than the other alternative: set.
+                mIterator =
+                    mKeys.stream().filter(k -> keepKey(k)).collect(Collectors.toList()).iterator();
+            }
+
+            @Override
+            public boolean hasNext() {
+                return mIterator.hasNext();
+            }
+
+            @Override
+            public String next() {
+                mLast = mIterator.next();
+                return mapKeyToItem(mLast);
+            }
+
+            @Override
+            public void remove() {
+                mIterator.remove();
+                mMap.remove(mLast);
+            }
+        }
+
+        @Override
+        public Iterator<String> iterator() {
+            return new KeyIterator();
+        }
+
+        @Override
+        public int size() {
+            return (int)mKeys.stream().filter(k -> keepKey(k)).count();
+        }
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+     * don't start with a prefix, such as "feature-"
+     */
+    private class UnprefixedKeySet extends FilteredMappedKeySet {
+        private String mPrefix;
+
+        public UnprefixedKeySet(String prefix) {
+            super();
+            mPrefix = prefix;
+        }
+
+        protected boolean keepKey(String key) {
+            return !key.startsWith(mPrefix);
+        }
+
+        protected String mapKeyToItem(String key) {
+            return key;
+        }
+
+        protected String mapItemToKey(String item) {
+            return item;
+        }
+    }
+
+    /**
+     * A Partial set view for a portion of the keys in a MediaFormat object for keys that
+     * start with a prefix, such as "feature-", with the prefix removed
+     */
+    private class PrefixedKeySetWithPrefixRemoved extends FilteredMappedKeySet {
+        private String mPrefix;
+        private int mPrefixLength;
+
+        public PrefixedKeySetWithPrefixRemoved(String prefix) {
+            super();
+            mPrefix = prefix;
+            mPrefixLength = prefix.length();
+        }
+
+        protected boolean keepKey(String key) {
+            return key.startsWith(mPrefix);
+        }
+
+        protected String mapKeyToItem(String key) {
+            return key.substring(mPrefixLength);
+        }
+
+        protected String mapItemToKey(String item) {
+            return mPrefix + item;
+        }
+    }
+
+
+   /**
+     * Returns a {@link java.util.Set Set} view of the keys contained in this MediaFormat.
+     *
+     * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+     * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+     * (except through the iterator's own remove operation), the results of the iteration are
+     * undefined. The set supports element removal, which removes the corresponding mapping from the
+     * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+     * It does not support the add or addAll operations.
+     */
+    public final @NonNull java.util.Set<String> getKeys() {
+        return new UnprefixedKeySet(KEY_FEATURE_);
+    }
+
+   /**
+     * Returns a {@link java.util.Set Set} view of the features contained in this MediaFormat.
+     *
+     * The set is backed by the MediaFormat object, so changes to the format are reflected in the
+     * set, and vice-versa. If the format is modified while an iteration over the set is in progress
+     * (except through the iterator's own remove operation), the results of the iteration are
+     * undefined. The set supports element removal, which removes the corresponding mapping from the
+     * format, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
+     * It does not support the add or addAll operations.
+     */
+    public final @NonNull java.util.Set<String> getFeatures() {
+        return new PrefixedKeySetWithPrefixRemoved(KEY_FEATURE_);
+    }
+
+    /**
+     * Create a copy of a media format object.
+     */
+    public MediaFormat(@NonNull MediaFormat other) {
+        mMap.putAll(other.mMap);
+    }
+
+    /**
      * Sets whether a feature is to be enabled ({@code true}) or disabled
      * ({@code false}).
      *
@@ -1102,7 +1458,7 @@
      * @see MediaCodecList#findEncoderForFormat
      * @see MediaCodecInfo.CodecCapabilities#isFormatSupported
      */
-    public void setFeatureEnabled(String feature, boolean enabled) {
+    public void setFeatureEnabled(@NonNull String feature, boolean enabled) {
         setInteger(KEY_FEATURE_ + feature, enabled ? 1 : 0);
     }
 
@@ -1112,8 +1468,8 @@
      * @param sampleRate The sampling rate of the content.
      * @param channelCount The number of audio channels in the content.
      */
-    public static final MediaFormat createAudioFormat(
-            String mime,
+    public static final @NonNull MediaFormat createAudioFormat(
+            @NonNull String mime,
             int sampleRate,
             int channelCount) {
         MediaFormat format = new MediaFormat();
@@ -1132,8 +1488,8 @@
      *        in the content.  (This will also work if there are multiple language
      *        tracks in the content.)
      */
-    public static final MediaFormat createSubtitleFormat(
-            String mime,
+    public static final @NonNull MediaFormat createSubtitleFormat(
+            @NonNull String mime,
             String language) {
         MediaFormat format = new MediaFormat();
         format.setString(KEY_MIME, mime);
@@ -1148,8 +1504,8 @@
      * @param width The width of the content (in pixels)
      * @param height The height of the content (in pixels)
      */
-    public static final MediaFormat createVideoFormat(
-            String mime,
+    public static final @NonNull MediaFormat createVideoFormat(
+            @NonNull String mime,
             int width,
             int height) {
         MediaFormat format = new MediaFormat();
@@ -1161,7 +1517,7 @@
     }
 
     @Override
-    public String toString() {
+    public @NonNull String toString() {
         return mMap.toString();
     }
 }
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 8288976..a10b212 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringDef;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningAppProcessInfo;
@@ -28,6 +29,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.media.MediaPlayer2.DrmInfo;
 import android.media.MediaPlayer2Proto.PlayerMessage;
 import android.media.MediaPlayer2Proto.Value;
 import android.net.Uri;
@@ -35,6 +37,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.util.Log;
@@ -45,8 +48,6 @@
 import com.android.framework.protobuf.InvalidProtocolBufferException;
 import com.android.internal.annotations.GuardedBy;
 
-import dalvik.system.CloseGuard;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
@@ -70,7 +71,11 @@
 import java.util.Queue;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -298,21 +303,7 @@
     private volatile float mVolume = 1.0f;
     private VideoSize mVideoSize = new VideoSize(0, 0);
 
-    // TODO: create per-source drm fields in SourceInfo
-    // Modular DRM
-    private final Object mDrmLock = new Object();
-    //--- guarded by |mDrmLock| start
-    private UUID mDrmUUID;
-    private DrmInfo mDrmInfo;
-    private MediaDrm mDrmObj;
-    private byte[] mDrmSessionId;
-    private boolean mDrmInfoResolved;
-    private boolean mActiveDrmScheme;
-    private boolean mDrmConfigAllowed;
-    private boolean mDrmProvisioningInProgress;
-    private boolean mPrepareDrmInProgress;
-    private ProvisioningThread mDrmProvisioningThread;
-    //--- guarded by |mDrmLock| end
+    private ExecutorService mDrmThreadPool = Executors.newCachedThreadPool();
 
     // Creating a dummy audio track, used for keeping session id alive
     private final Object mSessionIdLock = new Object();
@@ -326,6 +317,7 @@
     private final List<Task> mPendingTasks = new LinkedList<>();
     @GuardedBy("mTaskLock")
     private Task mCurrentTask;
+    private final AtomicLong mTaskIdGenerator = new AtomicLong(0);
 
     @GuardedBy("mTaskLock")
     boolean mIsPreviousCommandSeekTo = false;
@@ -411,12 +403,13 @@
             mHandlerThread = null;
         }
 
+        clearSourceInfos();
+
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
         synchronized (mDrmEventCbLock) {
             mDrmEventCallbackRecords.clear();
         }
-        resetDrmState();
 
         native_release();
 
@@ -456,15 +449,8 @@
         synchronized (mDrmEventCbLock) {
             mDrmEventCallbackRecords.clear();
         }
-        synchronized (mSrcLock) {
-            mCurrentSourceInfo = null;
-            mNextSourceInfos.clear();
-        }
 
-        synchronized (mTaskLock) {
-            mPendingTasks.clear();
-            mIsPreviousCommandSeekTo = false;
-        }
+        clearSourceInfos();
 
         stayAwake(false);
         native_reset();
@@ -478,7 +464,6 @@
             mTaskHandler.removeCallbacksAndMessages(null);
         }
 
-        resetDrmState();
     }
 
     private native void native_reset();
@@ -684,6 +669,8 @@
 
     /**
      * Sets the data source as described by a DataSourceDesc.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsd the descriptor of data source you want to play
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -693,23 +680,30 @@
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
-                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 int state = getState();
-                if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
-                    throw new IllegalStateException("called in wrong state " + state);
-                }
+                try {
+                    if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
+                        throw new IllegalStateException("called in wrong state " + state);
+                    }
 
-                synchronized (mSrcLock) {
-                    mCurrentSourceInfo = new SourceInfo(dsd);
-                    handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+                    synchronized (mSrcLock) {
+                        setCurrentSourceInfo_l(new SourceInfo(dsd));
+                        handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId);
+                    }
+                } finally {
+                    dsd.close();
                 }
             }
+
         });
     }
 
     /**
      * Sets a single data source as described by a DataSourceDesc which will be played
      * after current data source is finished.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsd the descriptor of data source you want to play after current one
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -719,9 +713,9 @@
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
-                    mNextSourceInfos.clear();
+                    clearNextSourceInfos_l();
                     mNextSourceInfos.add(new SourceInfo(dsd));
                 }
                 prepareNextDataSource();
@@ -731,6 +725,8 @@
 
     /**
      * Sets a list of data sources to be played sequentially after current data source is done.
+     * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor}
+     * in the {@link FileDataSourceDesc} will be closed by the player.
      *
      * @param dsds the list of data sources you want to play after current one
      * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
@@ -743,17 +739,15 @@
                 if (dsds == null || dsds.size() == 0) {
                     throw new IllegalArgumentException("data source list cannot be null or empty.");
                 }
-                for (DataSourceDesc dsd : dsds) {
-                    if (dsd == null) {
-                        throw new IllegalArgumentException(
-                                "DataSourceDesc in the source list cannot be null.");
-                    }
-                }
 
                 synchronized (mSrcLock) {
-                    mNextSourceInfos.clear();
+                    clearNextSourceInfos_l();
                     for (DataSourceDesc dsd : dsds) {
-                        mNextSourceInfos.add(new SourceInfo(dsd));
+                        if (dsd != null) {
+                            mNextSourceInfos.add(new SourceInfo(dsd));
+                        } else {
+                            Log.w(TAG, "DataSourceDesc in the source list shall not be null.");
+                        }
                     }
                 }
                 prepareNextDataSource();
@@ -770,7 +764,9 @@
         return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) {
             @Override
             void process() {
-                mNextSourceInfos.clear();
+                synchronized (mSrcLock) {
+                    clearNextSourceInfos_l();
+                }
             }
         });
     }
@@ -788,20 +784,20 @@
 
     private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
             throws IOException {
-        checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+        Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
 
         if (dsd instanceof CallbackDataSourceDesc) {
             CallbackDataSourceDesc cbDSD = (CallbackDataSourceDesc) dsd;
             handleDataSource(isCurrent,
                              srcId,
-                             cbDSD.getMedia2DataSource(),
+                             cbDSD.getDataSourceCallback(),
                              cbDSD.getStartPosition(),
                              cbDSD.getEndPosition());
         } else if (dsd instanceof FileDataSourceDesc) {
             FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd;
             handleDataSource(isCurrent,
                              srcId,
-                             fileDSD.getFileDescriptor(),
+                             fileDSD.getParcelFileDescriptor(),
                              fileDSD.getOffset(),
                              fileDSD.getLength(),
                              fileDSD.getStartPosition(),
@@ -885,7 +881,7 @@
             if (afd.getDeclaredLength() < 0) {
                 handleDataSource(isCurrent,
                         srcId,
-                        afd.getFileDescriptor(),
+                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                         0,
                         DataSourceDesc.LONG_MAX,
                         startPos,
@@ -893,7 +889,7 @@
             } else {
                 handleDataSource(isCurrent,
                         srcId,
-                        afd.getFileDescriptor(),
+                        ParcelFileDescriptor.dup(afd.getFileDescriptor()),
                         afd.getStartOffset(),
                         afd.getDeclaredLength(),
                         startPos,
@@ -959,7 +955,8 @@
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
-            handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos);
+            handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd),
+                    0, DataSourceDesc.LONG_MAX, startPos, endPos);
             is.close();
         } else {
             throw new IOException("handleDataSource failed.");
@@ -983,9 +980,10 @@
      */
     private void handleDataSource(
             boolean isCurrent, long srcId,
-            FileDescriptor fd, long offset, long length,
+            ParcelFileDescriptor pfd, long offset, long length,
             long startPos, long endPos) throws IOException {
-        nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos);
+        nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length,
+                startPos, endPos);
     }
 
     private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId,
@@ -994,15 +992,15 @@
 
     /**
      * @throws IllegalStateException if it is called in an invalid state
-     * @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
+     * @throws IllegalArgumentException if dataSource is not a valid DataSourceCallback
      */
-    private void handleDataSource(boolean isCurrent, long srcId, Media2DataSource dataSource,
+    private void handleDataSource(boolean isCurrent, long srcId, DataSourceCallback dataSource,
             long startPos, long endPos) {
         nativeHandleDataSourceCallback(isCurrent, srcId, dataSource, startPos, endPos);
     }
 
     private native void nativeHandleDataSourceCallback(
-            boolean isCurrent, long srcId, Media2DataSource dataSource,
+            boolean isCurrent, long srcId, DataSourceCallback dataSource,
             long startPos, long endPos);
 
     // return true if there is a next data source, false otherwise.
@@ -1036,7 +1034,10 @@
                         MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
                 mTaskHandler.handleMessage(msg, nextSource.mId);
 
-                mNextSourceInfos.poll();
+                SourceInfo nextSourceInfo = mNextSourceInfos.poll();
+                if (nextSource != null) {
+                    nextSourceInfo.close();
+                }
                 return prepareNextDataSource();
             }
         }
@@ -1057,7 +1058,7 @@
                 SourceInfo nextSourceInfo = mNextSourceInfos.peek();
                 if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) {
                     // Switch to next source only when it has been prepared.
-                    mCurrentSourceInfo = mNextSourceInfos.poll();
+                    setCurrentSourceInfo_l(mNextSourceInfos.poll());
 
                     long srcId = mCurrentSourceInfo.mId;
                     try {
@@ -1513,7 +1514,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the BufferingParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the BufferingParams cannot be null");
                 native_setBufferingParams(params);
             }
         });
@@ -1536,7 +1537,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the PlaybackParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the PlaybackParams cannot be null");
                 native_setPlaybackParams(params);
             }
         });
@@ -1564,7 +1565,7 @@
         return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
-                checkArgument(params != null, "the SyncParams cannot be null");
+                Media2Utils.checkArgument(params != null, "the SyncParams cannot be null");
                 native_setSyncParams(params);
             }
         });
@@ -2157,7 +2158,7 @@
             final int what = msg.arg1;
             final int extra = msg.arg2;
 
-            final SourceInfo sourceInfo = getSourceInfoById(srcId);
+            final SourceInfo sourceInfo = getSourceInfo(srcId);
             if (sourceInfo == null) {
                 return;
             }
@@ -2211,11 +2212,11 @@
                         Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
                     } else if (msg.obj instanceof byte[]) {
                         // The PlayerMessage was parsed already in postEventFromNative
-                        final DrmInfo drmInfo;
 
-                        synchronized (mDrmLock) {
-                            if (mDrmInfo != null) {
-                                drmInfo = mDrmInfo.makeCopy();
+                        final DrmInfo drmInfo;
+                        synchronized (sourceInfo) {
+                            if (sourceInfo.mDrmInfo != null) {
+                                drmInfo = sourceInfo.mDrmInfo.makeCopy();
                             } else {
                                 drmInfo = null;
                             }
@@ -2287,7 +2288,7 @@
                         }
                     });
 
-                    SourceInfo src = getSourceInfoById(srcId);
+                    SourceInfo src = getSourceInfo(srcId);
                     if (src != null) {
                         src.mBufferedPercentage.set(percent);
                     }
@@ -2488,6 +2489,7 @@
             return;
         }
 
+        final SourceInfo sourceInfo = mp.getSourceInfo(srcId);
         switch (what) {
             case MEDIA_DRM_INFO:
                 // We need to derive mDrmInfo before prepare() returns so processing it here
@@ -2495,7 +2497,7 @@
                 // notification looper so its handleMessage might process the event after prepare()
                 // has returned.
                 Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
-                if (obj != null) {
+                if (obj != null && sourceInfo != null) {
                     PlayerMessage playerMsg;
                     try {
                         playerMsg = PlayerMessage.parseFrom(obj);
@@ -2504,11 +2506,12 @@
                         break;
                     }
                     DrmInfo drmInfo = new DrmInfo(playerMsg);
-                    synchronized (mp.mDrmLock) {
-                        mp.mDrmInfo = drmInfo;
+                    synchronized (sourceInfo) {
+                        sourceInfo.mDrmInfo = drmInfo;
                     }
                 } else {
-                    Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
+                    Log.w(TAG, "MEDIA_DRM_INFO sourceInfo " + sourceInfo
+                            + " msg.obj of unexpected type " + obj);
                 }
                 break;
 
@@ -2517,8 +2520,10 @@
                 // mainly for prepare() use case. For prepare(), this still can run to a race
                 // condition b/c MediaPlayerNative releases the prepare() lock before calling notify
                 // so we also set mDrmInfoResolved in prepare().
-                synchronized (mp.mDrmLock) {
-                    mp.mDrmInfoResolved = true;
+                if (sourceInfo != null) {
+                    synchronized (sourceInfo) {
+                        sourceInfo.mDrmInfoResolved = true;
+                    }
                 }
                 break;
         }
@@ -2690,12 +2695,6 @@
         }
     }
 
-    private static void checkArgument(boolean expression, String errorMessage) {
-        if (!expression) {
-            throw new IllegalArgumentException(errorMessage);
-        }
-    }
-
     private void sendEvent(final EventNotifier notifier) {
         synchronized (mEventCbLock) {
             try {
@@ -3201,9 +3200,7 @@
      */
     // This is a synchronous call.
     public void setOnDrmConfigHelper(OnDrmConfigHelper listener) {
-        synchronized (mDrmLock) {
-            mOnDrmConfigHelper = listener;
-        }
+        mOnDrmConfigHelper = listener;
     }
 
     private OnDrmConfigHelper mOnDrmConfigHelper;
@@ -3321,6 +3318,25 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface PrepareDrmStatusCode {}
 
+    /** @hide */
+    @IntDef({
+        MediaDrm.KEY_TYPE_STREAMING,
+        MediaDrm.KEY_TYPE_OFFLINE,
+        MediaDrm.KEY_TYPE_RELEASE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaDrmKeyType {}
+
+    /** @hide */
+    @StringDef({
+        MediaDrm.PROPERTY_VENDOR,
+        MediaDrm.PROPERTY_VERSION,
+        MediaDrm.PROPERTY_DESCRIPTION,
+        MediaDrm.PROPERTY_ALGORITHMS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MediaDrmStringProperty {}
+
     /**
      * Retrieves the DRM Info associated with the given source
      *
@@ -3329,24 +3345,27 @@
      * @throws IllegalStateException if called before being prepared
      */
     public DrmInfo getDrmInfo(@NonNull DataSourceDesc dsd) {
-        // TODO: this implementation only works when dsd is the only data source
-        DrmInfo drmInfo = null;
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            DrmInfo drmInfo = null;
 
-        // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet;
-        // regardless below returns drmInfo anyway instead of raising an exception
-        synchronized (mDrmLock) {
-            if (!mDrmInfoResolved && mDrmInfo == null) {
-                final String msg = "The Player has not been prepared yet";
-                Log.v(TAG, msg);
-                throw new IllegalStateException(msg);
-            }
+            // there is not much point if the app calls getDrmInfo within an OnDrmInfoListener;
+            // regardless below returns drmInfo anyway instead of raising an exception
+            synchronized (sourceInfo) {
+                if (!sourceInfo.mDrmInfoResolved && sourceInfo.mDrmInfo == null) {
+                    final String msg = "The Player has not been prepared yet";
+                    Log.v(TAG, msg);
+                    throw new IllegalStateException(msg);
+                }
 
-            if (mDrmInfo != null) {
-                drmInfo = mDrmInfo.makeCopy();
-            }
-        }  // synchronized
+                if (sourceInfo.mDrmInfo != null) {
+                    drmInfo  = sourceInfo.mDrmInfo.makeCopy();
+                }
+            }   // synchronized
 
-        return drmInfo;
+            return drmInfo;
+        }
+        return null;
     }
 
     /**
@@ -3382,15 +3401,28 @@
      */
     // This is an asynchronous call.
     public Object prepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid) {
-        // TODO: this implementation only works when dsd is the only data source
         return addTask(new Task(CALL_COMPLETED_PREPARE_DRM, true) {
             @Override
             void process() {
-                int status = PREPARE_DRM_STATUS_SUCCESS;
+                final SourceInfo sourceInfo = getSourceInfo(dsd);
+                int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
                 boolean sendEvent = true;
 
+                if (sourceInfo == null) {
+                    Log.e(TAG, "prepareDrm(): DataSource not found.");
+                } else if (sourceInfo.mDrmInfo == null) {
+                    // only allowing if tied to a protected source;
+                    // might relax for releasing offline keys
+                    Log.e(TAG, "prepareDrm(): Wrong usage: The player must be prepared and "
+                            + "DRM info be retrieved before this call.");
+                } else {
+                    status = PREPARE_DRM_STATUS_SUCCESS;
+                }
+
                 try {
-                    doPrepareDrm(dsd, uuid);
+                    if (status == PREPARE_DRM_STATUS_SUCCESS) {
+                        sourceInfo.mDrmHandle.prepare(uuid);
+                    }
                 } catch (ResourceBusyException e) {
                     status = PREPARE_DRM_STATUS_RESOURCE_BUSY;
                 } catch (UnsupportedSchemeException e) {
@@ -3399,14 +3431,14 @@
                     Log.w(TAG, "prepareDrm: NotProvisionedException");
 
                     // handle provisioning internally; it'll reset mPrepareDrmInProgress
-                    status = handleProvisioninig(dsd, uuid);
+                    status = sourceInfo.mDrmHandle.handleProvisioninig(uuid, mTaskId);
 
                     if (status == PREPARE_DRM_STATUS_SUCCESS) {
                         // DrmEventCallback will be fired in provisioning
                         sendEvent = false;
                     } else {
-                        synchronized (mDrmLock) {
-                            cleanDrmObj();
+                        synchronized (sourceInfo.mDrmHandle) {
+                            sourceInfo.mDrmHandle.cleanDrmObj();
                         }
 
                         switch (status) {
@@ -3449,95 +3481,6 @@
         });
     }
 
-    private void doPrepareDrm(@NonNull DataSourceDesc dsd, @NonNull UUID uuid)
-            throws UnsupportedSchemeException, ResourceBusyException,
-                   NotProvisionedException {
-        Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
-
-        synchronized (mDrmLock) {
-            // only allowing if tied to a protected source; might relax for releasing offline keys
-            if (mDrmInfo == null) {
-                final String msg = "prepareDrm(): Wrong usage: The player must be prepared and "
-                        + "DRM info be retrieved before this call.";
-                Log.e(TAG, msg);
-                throw new IllegalStateException(msg);
-            }
-
-            if (mActiveDrmScheme) {
-                final String msg = "prepareDrm(): Wrong usage: There is already "
-                        + "an active DRM scheme with " + mDrmUUID;
-                Log.e(TAG, msg);
-                throw new IllegalStateException(msg);
-            }
-
-            if (mPrepareDrmInProgress) {
-                final String msg = "prepareDrm(): Wrong usage: There is already "
-                        + "a pending prepareDrm call.";
-                Log.e(TAG, msg);
-                throw new IllegalStateException(msg);
-            }
-
-            if (mDrmProvisioningInProgress) {
-                final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
-                Log.e(TAG, msg);
-                throw new IllegalStateException(msg);
-            }
-
-            // shouldn't need this; just for safeguard
-            cleanDrmObj();
-
-            mPrepareDrmInProgress = true;
-
-            try {
-                // only creating the DRM object to allow pre-openSession configuration
-                prepareDrm_createDrmStep(uuid);
-            } catch (Exception e) {
-                Log.w(TAG, "prepareDrm(): Exception ", e);
-                mPrepareDrmInProgress = false;
-                throw e;
-            }
-
-            mDrmConfigAllowed = true;
-        }  // synchronized
-
-        // call the callback outside the lock
-        if (mOnDrmConfigHelper != null)  {
-            mOnDrmConfigHelper.onDrmConfig(this, dsd);
-        }
-
-        synchronized (mDrmLock) {
-            mDrmConfigAllowed = false;
-            boolean earlyExit = false;
-
-            try {
-                prepareDrm_openSessionStep(uuid);
-
-                mDrmUUID = uuid;
-                mActiveDrmScheme = true;
-                mPrepareDrmInProgress = false;
-            } catch (IllegalStateException e) {
-                final String msg = "prepareDrm(): Wrong usage: The player must be "
-                        + "in the prepared state to call prepareDrm().";
-                Log.e(TAG, msg);
-                earlyExit = true;
-                mPrepareDrmInProgress = false;
-                throw new IllegalStateException(msg);
-            } catch (NotProvisionedException e) {
-                Log.w(TAG, "prepareDrm: NotProvisionedException", e);
-                throw e;
-            } catch (Exception e) {
-                Log.e(TAG, "prepareDrm: Exception " + e);
-                earlyExit = true;
-                mPrepareDrmInProgress = false;
-                throw e;
-            } finally {
-                if (earlyExit) {  // clean up object if didn't succeed
-                    cleanDrmObj();
-                }
-            }  // finally
-        }  // synchronized
-    }
-
     /**
      * Releases the DRM session for the given data source
      * <p>
@@ -3552,35 +3495,10 @@
     // This is a synchronous call.
     public void releaseDrm(@NonNull DataSourceDesc dsd)
             throws NoDrmSchemeException {
-        // TODO: this implementation only works when dsd is the only data source
-        synchronized (mDrmLock) {
-            Log.v(TAG, "releaseDrm:");
-
-            if (!mActiveDrmScheme) {
-                Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
-                throw new NoDrmSchemeException(
-                        "releaseDrm: No active DRM scheme to release.");
-            }
-
-            try {
-                // we don't have the player's state in this layer. The below call raises
-                // exception if we're in a non-stopped/prepared state.
-
-                // for cleaning native/mediaserver crypto object
-                native_releaseDrm();
-
-                // for cleaning client-side MediaDrm object; only called if above has succeeded
-                cleanDrmObj();
-
-                mActiveDrmScheme = false;
-            } catch (IllegalStateException e) {
-                Log.w(TAG, "releaseDrm: Exception ", e);
-                throw new IllegalStateException(
-                        "releaseDrm: The player is not in a valid state.");
-            } catch (Exception e) {
-                Log.e(TAG, "releaseDrm: Exception ", e);
-            }
-        }  // synchronized
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            sourceInfo.mDrmHandle.release();
+        }
     }
 
     private native void native_releaseDrm();
@@ -3624,51 +3542,22 @@
      *
      * @throws NoDrmSchemeException if there is no active DRM session
      */
-    @NonNull
     public MediaDrm.KeyRequest getDrmKeyRequest(
             @NonNull DataSourceDesc dsd,
             @Nullable byte[] keySetId, @Nullable byte[] initData,
-            @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+            @Nullable String mimeType, @MediaDrmKeyType int keyType,
             @Nullable Map<String, String> optionalParameters)
             throws NoDrmSchemeException {
-        // TODO: this implementation only works when dsd is the only data source
-        Log.v(TAG, "getDrmKeyRequest: "
-                + " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType
-                + " keyType: " + keyType + " optionalParameters: " + optionalParameters);
+        Log.v(TAG, "getDrmKeyRequest: " +
+                " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
+                " keyType: " + keyType + " optionalParameters: " + optionalParameters);
 
-        synchronized (mDrmLock) {
-            if (!mActiveDrmScheme) {
-                Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeException(
-                        "getDrmKeyRequest: Has to set a DRM scheme first.");
-            }
-
-            try {
-                byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE)
-                        ? mDrmSessionId :  // sessionId for KEY_TYPE_STREAMING/OFFLINE
-                        keySetId;  // keySetId for KEY_TYPE_RELEASE
-
-                HashMap<String, String> hmapOptionalParameters =
-                                                (optionalParameters != null)
-                                                ? new HashMap<String, String>(optionalParameters) :
-                                                null;
-
-                MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
-                                                              keyType, hmapOptionalParameters);
-                Log.v(TAG, "getDrmKeyRequest:   --> request: " + request);
-
-                return request;
-
-            } catch (NotProvisionedException e) {
-                Log.w(TAG, "getDrmKeyRequest NotProvisionedException: "
-                        + "Unexpected. Shouldn't have reached here.");
-                throw new IllegalStateException("getDrmKeyRequest: Unexpected provisioning error.");
-            } catch (Exception e) {
-                Log.w(TAG, "getDrmKeyRequest Exception " + e);
-                throw e;
-            }
-
-        }  // synchronized
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            return sourceInfo.mDrmHandle.getDrmKeyRequest(
+                    keySetId, initData, mimeType, keyType, optionalParameters);
+        }
+        return null;
     }
 
     /**
@@ -3698,40 +3587,13 @@
             @NonNull DataSourceDesc dsd,
             @Nullable byte[] keySetId, @NonNull byte[] response)
             throws NoDrmSchemeException, DeniedByServerException {
-        // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response);
 
-        synchronized (mDrmLock) {
-
-            if (!mActiveDrmScheme) {
-                Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
-                throw new NoDrmSchemeException(
-                        "getDrmKeyRequest: Has to set a DRM scheme first.");
-            }
-
-            try {
-                byte[] scope = (keySetId == null)
-                                ? mDrmSessionId :     // sessionId for KEY_TYPE_STREAMING/OFFLINE
-                                keySetId;           // keySetId for KEY_TYPE_RELEASE
-
-                byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
-
-                Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId + " response: " + response
-                        + " --> " + keySetResult);
-
-
-                return keySetResult;
-
-            } catch (NotProvisionedException e) {
-                Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: "
-                        + "Unexpected. Shouldn't have reached here.");
-                throw new IllegalStateException("provideDrmKeyResponse: "
-                        + "Unexpected provisioning error.");
-            } catch (Exception e) {
-                Log.w(TAG, "provideDrmKeyResponse Exception " + e);
-                throw e;
-            }
-        }  // synchronized
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            return sourceInfo.mDrmHandle.provideDrmKeyResponse(keySetId, response);
+        }
+        return null;
     }
 
     /**
@@ -3750,23 +3612,12 @@
             @NonNull DataSourceDesc dsd,
             @NonNull byte[] keySetId)
             throws NoDrmSchemeException {
-        // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "restoreDrmKeys: keySetId: " + keySetId);
 
-        synchronized (mDrmLock) {
-            if (!mActiveDrmScheme) {
-                Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
-                throw new NoDrmSchemeException(
-                        "restoreDrmKeys: Has to set a DRM scheme first.");
-            }
-
-            try {
-                mDrmObj.restoreKeys(mDrmSessionId, keySetId);
-            } catch (Exception e) {
-                Log.w(TAG, "restoreKeys Exception " + e);
-                throw e;
-            }
-        }  // synchronized
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            sourceInfo.mDrmHandle.restoreDrmKeys(keySetId);
+        }
     }
 
     /**
@@ -3783,34 +3634,17 @@
      *
      * @throws NoDrmSchemeException if there is no active DRM session
      */
-    @NonNull
     public String getDrmPropertyString(
             @NonNull DataSourceDesc dsd,
-            @NonNull @MediaDrm.StringProperty String propertyName)
+            @NonNull @MediaDrmStringProperty String propertyName)
             throws NoDrmSchemeException {
-        // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
 
-        String value;
-        synchronized (mDrmLock) {
-
-            if (!mActiveDrmScheme && !mDrmConfigAllowed) {
-                Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeException(
-                        "getDrmPropertyString: Has to prepareDrm() first.");
-            }
-
-            try {
-                value = mDrmObj.getPropertyString(propertyName);
-            } catch (Exception e) {
-                Log.w(TAG, "getDrmPropertyString Exception " + e);
-                throw e;
-            }
-        }  // synchronized
-
-        Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
-
-        return value;
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            return sourceInfo.mDrmHandle.getDrmPropertyString(propertyName);
+        }
+        return null;
     }
 
     /**
@@ -3829,26 +3663,15 @@
     // This is a synchronous call.
     public void setDrmPropertyString(
             @NonNull DataSourceDesc dsd,
-            @NonNull @MediaDrm.StringProperty String propertyName, @NonNull String value)
+            @NonNull @MediaDrmStringProperty String propertyName, @NonNull String value)
             throws NoDrmSchemeException {
         // TODO: this implementation only works when dsd is the only data source
         Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
 
-        synchronized (mDrmLock) {
-
-            if (!mActiveDrmScheme && !mDrmConfigAllowed) {
-                Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
-                throw new NoDrmSchemeException(
-                        "setDrmPropertyString: Has to prepareDrm() first.");
-            }
-
-            try {
-                mDrmObj.setPropertyString(propertyName, value);
-            } catch (Exception e) {
-                Log.w(TAG, "setDrmPropertyString Exception " + e);
-                throw e;
-            }
-        }  // synchronized
+        final SourceInfo sourceInfo = getSourceInfo(dsd);
+        if (sourceInfo != null) {
+            sourceInfo.mDrmHandle.setDrmPropertyString(propertyName, value);
+        }
     }
 
     /**
@@ -4000,43 +3823,6 @@
 
     private native void native_prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
 
-    // Modular DRM helpers
-
-    private void prepareDrm_createDrmStep(@NonNull UUID uuid)
-            throws UnsupportedSchemeException {
-        Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
-
-        try {
-            mDrmObj = new MediaDrm(uuid);
-            Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
-        } catch (Exception e) { // UnsupportedSchemeException
-            Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
-            throw e;
-        }
-    }
-
-    private void prepareDrm_openSessionStep(@NonNull UUID uuid)
-            throws NotProvisionedException, ResourceBusyException {
-        Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
-
-        // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
-        // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
-        // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
-        try {
-            mDrmSessionId = mDrmObj.openSession();
-            Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
-
-            // Sending it down to native/mediaserver to create the crypto object
-            // This call could simply fail due to bad player state, e.g., after play().
-            native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
-            Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
-
-        } catch (Exception e) { //ResourceBusyException, NotProvisionedException
-            Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
-            throw e;
-        }
-    }
-
     // Instantiated from the native side
     @SuppressWarnings("unused")
     private static class StreamEventCallback extends AudioTrack.StreamEventCallback {
@@ -4068,227 +3854,28 @@
         }
     }
 
-    private class ProvisioningThread extends Thread {
-        public static final int TIMEOUT_MS = 60000;
-
-        private final DataSourceDesc mDSD;
-        private UUID mUuid;
-        private String mUrlStr;
-        private Object mDrmLock;
-        private MediaPlayer2 mMediaPlayer;
-        private int mStatus;
-        public  int status() {
-            return mStatus;
-        }
-
-        public ProvisioningThread(MediaDrm.ProvisionRequest request,
-                DataSourceDesc dsd,
-                UUID uuid, MediaPlayer2 mediaPlayer) {
-            // lock is held by the caller
-            mDSD = dsd;
-            mDrmLock = mediaPlayer.mDrmLock;
-            mMediaPlayer = mediaPlayer;
-
-            mUrlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
-            mUuid = uuid;
-
-            mStatus = PREPARE_DRM_STATUS_PREPARATION_ERROR;
-
-            Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + mUrlStr);
-        }
-
-        public void run() {
-
-            byte[] response = null;
-            boolean provisioningSucceeded = false;
-            try {
-                URL url = new URL(mUrlStr);
-                final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
-                try {
-                    connection.setRequestMethod("POST");
-                    connection.setDoOutput(false);
-                    connection.setDoInput(true);
-                    connection.setConnectTimeout(TIMEOUT_MS);
-                    connection.setReadTimeout(TIMEOUT_MS);
-
-                    connection.connect();
-                    response = readInputStreamFully(connection.getInputStream());
-
-                    Log.v(TAG, "handleProvisioninig: Thread run: response "
-                            + response.length + " " + response);
-                } catch (Exception e) {
-                    mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
-                    Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
-                } finally {
-                    connection.disconnect();
-                }
-            } catch (Exception e)   {
-                mStatus = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
-                Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
-            }
-
-            if (response != null) {
-                try {
-                    mDrmObj.provideProvisionResponse(response);
-                    Log.v(TAG, "handleProvisioninig: Thread run: "
-                            + "provideProvisionResponse SUCCEEDED!");
-
-                    provisioningSucceeded = true;
-                } catch (Exception e) {
-                    mStatus = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
-                    Log.w(TAG, "handleProvisioninig: Thread run: "
-                            + "provideProvisionResponse " + e);
-                }
-            }
-
-            boolean succeeded = false;
-
-            synchronized (mDrmLock) {
-                // continuing with prepareDrm
-                if (provisioningSucceeded) {
-                    succeeded = mMediaPlayer.resumePrepareDrm(mUuid);
-                    mStatus = (succeeded)
-                            ? PREPARE_DRM_STATUS_SUCCESS :
-                            PREPARE_DRM_STATUS_PREPARATION_ERROR;
-                }
-                mMediaPlayer.mDrmProvisioningInProgress = false;
-                mMediaPlayer.mPrepareDrmInProgress = false;
-                if (!succeeded) {
-                    cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
-                }
-            }  // synchronized
-
-            // calling the callback outside the lock
-            sendDrmEvent(new DrmEventNotifier() {
-                @Override
-                public void notify(DrmEventCallback callback) {
-                    callback.onDrmPrepared(
-                            mMediaPlayer, mDSD, mStatus);
-                }
-            });
-
-            synchronized (mTaskLock) {
-                if (mCurrentTask != null
-                        && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
-                        && mCurrentTask.mNeedToWaitForEventToComplete) {
-                    mCurrentTask = null;
-                    processPendingTask_l();
-                }
-            }
-        }
-
-        /**
-         * Returns a byte[] containing the remainder of 'in', closing it when done.
-         */
-        private byte[] readInputStreamFully(InputStream in) throws IOException {
-            try {
-                return readInputStreamFullyNoClose(in);
-            } finally {
-                in.close();
-            }
-        }
-
-        /**
-         * Returns a byte[] containing the remainder of 'in'.
-         */
-        private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
-            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-            byte[] buffer = new byte[1024];
-            int count;
-            while ((count = in.read(buffer)) != -1) {
-                bytes.write(buffer, 0, count);
-            }
-            return bytes.toByteArray();
-        }
-    }  // ProvisioningThread
-
-    private int handleProvisioninig(DataSourceDesc dsd, UUID uuid) {
-        synchronized (mDrmLock) {
-            if (mDrmProvisioningInProgress) {
-                Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
-                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-            }
-
-            MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
-            if (provReq == null) {
-                Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
-                return PREPARE_DRM_STATUS_PREPARATION_ERROR;
-            }
-
-            Log.v(TAG, "handleProvisioninig provReq "
-                    + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
-
-            // networking in a background thread
-            mDrmProvisioningInProgress = true;
-
-            mDrmProvisioningThread = new ProvisioningThread(provReq, dsd, uuid, this);
-            mDrmProvisioningThread.start();
-
-            return PREPARE_DRM_STATUS_SUCCESS;
-        }
-    }
-
-    private boolean resumePrepareDrm(UUID uuid) {
-        Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
-
-        // mDrmLock is guaranteed to be held
-        boolean success = false;
+    /**
+     * Returns a byte[] containing the remainder of 'in', closing it when done.
+     */
+    private static byte[] readInputStreamFully(InputStream in) throws IOException {
         try {
-            // resuming
-            prepareDrm_openSessionStep(uuid);
-
-            mDrmUUID = uuid;
-            mActiveDrmScheme = true;
-
-            success = true;
-        } catch (Exception e) {
-            Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed with " + e);
-            // mDrmObj clean up is done by the caller
+            return readInputStreamFullyNoClose(in);
+        } finally {
+            in.close();
         }
-
-        return success;
     }
 
-    private void resetDrmState() {
-        synchronized (mDrmLock) {
-            Log.v(TAG, "resetDrmState:"
-                    + " mDrmInfo=" + mDrmInfo
-                    + " mDrmProvisioningThread=" + mDrmProvisioningThread
-                    + " mPrepareDrmInProgress=" + mPrepareDrmInProgress
-                    + " mActiveDrmScheme=" + mActiveDrmScheme);
-
-            mDrmInfoResolved = false;
-            mDrmInfo = null;
-
-            if (mDrmProvisioningThread != null) {
-                // timeout; relying on HttpUrlConnection
-                try {
-                    mDrmProvisioningThread.join();
-                } catch (InterruptedException e) {
-                    Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
-                }
-                mDrmProvisioningThread = null;
-            }
-
-            mPrepareDrmInProgress = false;
-            mActiveDrmScheme = false;
-
-            cleanDrmObj();
-        }  // synchronized
-    }
-
-    private void cleanDrmObj() {
-        // the caller holds mDrmLock
-        Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
-
-        if (mDrmSessionId != null)    {
-            mDrmObj.closeSession(mDrmSessionId);
-            mDrmSessionId = null;
+    /**
+     * Returns a byte[] containing the remainder of 'in'.
+     */
+    private static byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            bytes.write(buffer, 0, count);
         }
-        if (mDrmObj != null) {
-            mDrmObj.release();
-            mDrmObj = null;
-        }
+        return bytes.toByteArray();
     }
 
     private static byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
@@ -4304,8 +3891,6 @@
         return uuidBytes;
     }
 
-    // Modular DRM end
-
     private static class TimedTextUtil {
         // These keys must be in sync with the keys in TextDescription2.h
         private static final int KEY_START_TIME                     = 7; // int
@@ -4381,6 +3966,7 @@
     }
 
     private abstract class Task implements Runnable {
+        final long mTaskId = mTaskIdGenerator.getAndIncrement();
         private final int mMediaCallType;
         private final boolean mNeedToWaitForEventToComplete;
         private DataSourceDesc mDSD;
@@ -4472,10 +4058,507 @@
         }
     };
 
-    private final class SourceInfo {
+    // Modular DRM
+    final class DrmHandle {
+
+        static final int PROVISION_TIMEOUT_MS = 60000;
+
+        final DataSourceDesc mDSD;
+
+        //--- guarded by |this| start
+        MediaDrm mDrmObj;
+        byte[] mDrmSessionId;
+        UUID mActiveDrmUUID;
+        boolean mDrmConfigAllowed;
+        boolean mDrmProvisioningInProgress;
+        boolean mPrepareDrmInProgress;
+        Future<?> mProvisionResult;
+        //--- guarded by |this| end
+
+        DrmHandle(DataSourceDesc dsd) {
+            mDSD = dsd;
+        }
+
+        void prepare(UUID uuid) throws UnsupportedSchemeException,
+                ResourceBusyException, NotProvisionedException {
+            final OnDrmConfigHelper onDrmConfigHelper = mOnDrmConfigHelper;
+            Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + onDrmConfigHelper);
+
+            synchronized (this) {
+                if (mActiveDrmUUID != null) {
+                    final String msg = "prepareDrm(): Wrong usage: There is already "
+                            + "an active DRM scheme with " + uuid;
+                    Log.e(TAG, msg);
+                    throw new IllegalStateException(msg);
+                }
+
+                if (mPrepareDrmInProgress) {
+                    final String msg = "prepareDrm(): Wrong usage: There is already "
+                            + "a pending prepareDrm call.";
+                    Log.e(TAG, msg);
+                    throw new IllegalStateException(msg);
+                }
+
+                if (mDrmProvisioningInProgress) {
+                    final String msg = "prepareDrm(): Unexpectd: Provisioning already in progress";
+                    Log.e(TAG, msg);
+                    throw new IllegalStateException(msg);
+                }
+
+                // shouldn't need this; just for safeguard
+                cleanDrmObj();
+
+                mPrepareDrmInProgress = true;
+
+                try {
+                    // only creating the DRM object to allow pre-openSession configuration
+                    prepareDrm_createDrmStep(uuid);
+                } catch (Exception e) {
+                    Log.w(TAG, "prepareDrm(): Exception ", e);
+                    mPrepareDrmInProgress = false;
+                    throw e;
+                }
+
+                mDrmConfigAllowed = true;
+            }  // synchronized
+
+            // call the callback outside the lock
+            if (onDrmConfigHelper != null)  {
+                onDrmConfigHelper.onDrmConfig(MediaPlayer2.this, mDSD);
+            }
+
+            synchronized (this) {
+                mDrmConfigAllowed = false;
+                boolean earlyExit = false;
+
+                try {
+                    prepareDrm_openSessionStep(uuid);
+
+                    this.mActiveDrmUUID = uuid;
+                    mPrepareDrmInProgress = false;
+                } catch (IllegalStateException e) {
+                    final String msg = "prepareDrm(): Wrong usage: The player must be "
+                            + "in the prepared state to call prepareDrm().";
+                    Log.e(TAG, msg);
+                    earlyExit = true;
+                    mPrepareDrmInProgress = false;
+                    throw new IllegalStateException(msg);
+                } catch (NotProvisionedException e) {
+                    Log.w(TAG, "prepareDrm: NotProvisionedException", e);
+                    throw e;
+                } catch (Exception e) {
+                    Log.e(TAG, "prepareDrm: Exception " + e);
+                    earlyExit = true;
+                    mPrepareDrmInProgress = false;
+                    throw e;
+                } finally {
+                    if (earlyExit) {  // clean up object if didn't succeed
+                        cleanDrmObj();
+                    }
+                }  // finally
+            }  // synchronized
+        }
+
+        void prepareDrm_createDrmStep(UUID uuid)
+                throws UnsupportedSchemeException {
+            Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
+
+            try {
+                mDrmObj = new MediaDrm(uuid);
+                Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
+            } catch (Exception e) { // UnsupportedSchemeException
+                Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
+                throw e;
+            }
+        }
+
+        void prepareDrm_openSessionStep(UUID uuid)
+                throws NotProvisionedException, ResourceBusyException {
+            Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
+
+            // TODO:
+            // don't need an open session for a future specialKeyReleaseDrm mode but we should do
+            // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
+            // at prepareDrm/openSession rather than getDrmKeyRequest/provideDrmKeyResponse
+            try {
+                mDrmSessionId = mDrmObj.openSession();
+                Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
+
+                // Sending it down to native/mediaserver to create the crypto object
+                // This call could simply fail due to bad player state, e.g., after play().
+                MediaPlayer2.this.native_prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
+                Log.v(TAG, "prepareDrm_openSessionStep: native_prepareDrm/Crypto succeeded");
+
+            } catch (Exception e) { //ResourceBusyException, NotProvisionedException
+                Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
+                throw e;
+            }
+
+        }
+
+        int handleProvisioninig(UUID uuid, long taskId) {
+            synchronized (this) {
+                if (mDrmProvisioningInProgress) {
+                    Log.e(TAG, "handleProvisioninig: Unexpected mDrmProvisioningInProgress");
+                    return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+                }
+
+                MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+                if (provReq == null) {
+                    Log.e(TAG, "handleProvisioninig: getProvisionRequest returned null.");
+                    return PREPARE_DRM_STATUS_PREPARATION_ERROR;
+                }
+
+                Log.v(TAG, "handleProvisioninig provReq "
+                        + " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
+
+                // networking in a background thread
+                mDrmProvisioningInProgress = true;
+
+                mProvisionResult = mDrmThreadPool.submit(newProvisioningTask(uuid, taskId));
+
+                return PREPARE_DRM_STATUS_SUCCESS;
+            }
+        }
+
+        void provision(UUID uuid, long taskId) {
+
+            MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
+            String urlStr = provReq.getDefaultUrl();
+            urlStr += "&signedRequest=" + new String(provReq.getData());
+            Log.v(TAG, "handleProvisioninig: Thread is initialised url: " + urlStr);
+
+            byte[] response = null;
+            boolean provisioningSucceeded = false;
+            int status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+            try {
+                URL url = new URL(urlStr);
+                final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+                try {
+                    connection.setRequestMethod("POST");
+                    connection.setDoOutput(false);
+                    connection.setDoInput(true);
+                    connection.setConnectTimeout(PROVISION_TIMEOUT_MS);
+                    connection.setReadTimeout(PROVISION_TIMEOUT_MS);
+
+                    connection.connect();
+                    response = readInputStreamFully(connection.getInputStream());
+
+                    Log.v(TAG, "handleProvisioninig: Thread run: response " +
+                            response.length + " " + response);
+                } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+                    Log.w(TAG, "handleProvisioninig: Thread run: connect " + e + " url: " + url);
+                } finally {
+                    connection.disconnect();
+                }
+            } catch (Exception e)   {
+                status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
+                Log.w(TAG, "handleProvisioninig: Thread run: openConnection " + e);
+            }
+
+            if (response != null) {
+                try {
+                    mDrmObj.provideProvisionResponse(response);
+                    Log.v(TAG, "handleProvisioninig: Thread run: " +
+                            "provideProvisionResponse SUCCEEDED!");
+
+                    provisioningSucceeded = true;
+                } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
+                    Log.w(TAG, "handleProvisioninig: Thread run: " +
+                            "provideProvisionResponse " + e);
+                }
+            }
+
+            boolean succeeded = false;
+
+            synchronized (this) {
+                // continuing with prepareDrm
+                if (provisioningSucceeded) {
+                    succeeded = resumePrepare(uuid);
+                    status = (succeeded) ?
+                            PREPARE_DRM_STATUS_SUCCESS :
+                            PREPARE_DRM_STATUS_PREPARATION_ERROR;
+                }
+                mDrmProvisioningInProgress = false;
+                mPrepareDrmInProgress = false;
+                if (!succeeded) {
+                    cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
+                }
+            }  // synchronized
+
+            // calling the callback outside the lock
+            final int finalStatus = status;
+            sendDrmEvent(new DrmEventNotifier() {
+                @Override
+                public void notify(DrmEventCallback callback) {
+                    callback.onDrmPrepared(
+                            MediaPlayer2.this, mDSD, finalStatus);
+                }
+            });
+
+            synchronized (mTaskLock) {
+                if (mCurrentTask != null
+                        && mCurrentTask.mTaskId == taskId
+                        && mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE_DRM
+                        && mCurrentTask.mNeedToWaitForEventToComplete) {
+                    mCurrentTask = null;
+                    processPendingTask_l();
+                }
+            }
+        }
+
+        Runnable newProvisioningTask(UUID uuid, long taskId) {
+            return new Runnable() {
+                @Override
+                public void run() {
+                    provision(uuid, taskId);
+                }
+            };
+        }
+
+        boolean resumePrepare(UUID uuid) {
+            Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
+
+            // mDrmLock is guaranteed to be held
+            boolean success = false;
+            try {
+                // resuming
+                prepareDrm_openSessionStep(uuid);
+
+                this.mActiveDrmUUID = uuid;
+
+                success = true;
+            } catch (Exception e) {
+                Log.w(TAG, "handleProvisioninig: Thread run native_prepareDrm resume failed:" + e);
+                // mDrmObj clean up is done by the caller
+            }
+
+            return success;
+        }
+
+        void cleanDrmObj() {
+            // the caller holds mDrmLock
+            Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
+
+            if (mDrmSessionId != null)    {
+                mDrmObj.closeSession(mDrmSessionId);
+                mDrmSessionId = null;
+            }
+            if (mDrmObj != null) {
+                mDrmObj.close();
+                mDrmObj = null;
+            }
+        }
+
+        void release() throws NoDrmSchemeException {
+            synchronized (this) {
+                Log.v(TAG, "releaseDrm:");
+
+                if (mActiveDrmUUID == null) {
+                    Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
+                    throw new NoDrmSchemeException(
+                            "releaseDrm: No active DRM scheme to release.");
+                }
+
+                try {
+                    // we don't have the player's state in this layer. The below call raises
+                    // exception if we're in a non-stopped/prepared state.
+
+                    // for cleaning native/mediaserver crypto object
+                    native_releaseDrm();
+
+                    // for cleaning client-side MediaDrm object; only called if above has succeeded
+                    cleanDrmObj();
+
+                    this.mActiveDrmUUID = null;
+                } catch (IllegalStateException e) {
+                    Log.w(TAG, "releaseDrm: Exception ", e);
+                    throw new IllegalStateException(
+                            "releaseDrm: The player is not in a valid state.");
+                } catch (Exception e) {
+                    Log.e(TAG, "releaseDrm: Exception ", e);
+                }
+            }  // synchronized
+        }
+
+        void cleanup() {
+            synchronized (this) {
+                Log.v(TAG, "cleanupDrm: " +
+                        " mProvisioningTask=" + mProvisionResult +
+                        " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
+                        " mActiveDrmScheme=" + mActiveDrmUUID);
+
+                if (mProvisionResult != null) {
+                    // timeout; relying on HttpUrlConnection
+                    try {
+                        mProvisionResult.get();
+                    }
+                    catch (InterruptedException | ExecutionException e) {
+                        Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
+                    }
+                }
+
+                // set to false to avoid duplicate release calls
+                this.mActiveDrmUUID = null;
+
+                cleanDrmObj();
+            }   // synchronized
+        }
+
+        Runnable newCleanupTask() {
+            return new Runnable() {
+                @Override
+                public void run() {
+                    cleanup();
+                }
+            };
+        }
+
+        MediaDrm.KeyRequest getDrmKeyRequest(
+                byte[] keySetId, byte[] initData,
+                String mimeType, int keyType,
+                Map<String, String> optionalParameters)
+                throws NoDrmSchemeException {
+            synchronized (this) {
+                if (mActiveDrmUUID == null) {
+                    Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+                    throw new NoDrmSchemeException(
+                            "getDrmKeyRequest: Has to set a DRM scheme first.");
+                }
+
+                try {
+                    byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+                            mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+                            keySetId;                  // keySetId for KEY_TYPE_RELEASE
+
+                    HashMap<String, String> hmapOptionalParameters =
+                            (optionalParameters != null)
+                            ? new HashMap<String, String>(optionalParameters)
+                            : null;
+
+                    MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(
+                            scope, initData, mimeType, keyType, hmapOptionalParameters);
+                    Log.v(TAG, "getDrmKeyRequest:   --> request: " + request);
+
+                    return request;
+
+                } catch (NotProvisionedException e) {
+                    Log.w(TAG, "getDrmKeyRequest NotProvisionedException: " +
+                            "Unexpected. Shouldn't have reached here.");
+                    throw new IllegalStateException("getDrmKeyRequest: provisioning error.");
+                } catch (Exception e) {
+                    Log.w(TAG, "getDrmKeyRequest Exception " + e);
+                    throw e;
+                }
+
+            }
+        }
+
+        byte[] provideDrmKeyResponse(byte[] keySetId, byte[] response)
+                throws NoDrmSchemeException, DeniedByServerException {
+            synchronized (this) {
+
+                if (mActiveDrmUUID == null) {
+                    Log.e(TAG, "getDrmKeyRequest NoDrmSchemeException");
+                    throw new NoDrmSchemeException(
+                            "getDrmKeyRequest: Has to set a DRM scheme first.");
+                }
+
+                try {
+                    byte[] scope = (keySetId == null) ?
+                                    mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+                                    keySetId;                  // keySetId for KEY_TYPE_RELEASE
+
+                    byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
+
+                    Log.v(TAG, "provideDrmKeyResponse: keySetId: " + keySetId
+                            + " response: " + response + " --> " + keySetResult);
+
+
+                    return keySetResult;
+
+                } catch (NotProvisionedException e) {
+                    Log.w(TAG, "provideDrmKeyResponse NotProvisionedException: " +
+                            "Unexpected. Shouldn't have reached here.");
+                    throw new IllegalStateException("provideDrmKeyResponse: " +
+                            "Unexpected provisioning error.");
+                } catch (Exception e) {
+                    Log.w(TAG, "provideDrmKeyResponse Exception " + e);
+                    throw e;
+                }
+            }
+        }
+
+        void restoreDrmKeys(byte[] keySetId)
+                throws NoDrmSchemeException {
+            synchronized (this) {
+                if (mActiveDrmUUID == null) {
+                    Log.w(TAG, "restoreDrmKeys NoDrmSchemeException");
+                    throw new NoDrmSchemeException(
+                            "restoreDrmKeys: Has to set a DRM scheme first.");
+                }
+
+                try {
+                    mDrmObj.restoreKeys(mDrmSessionId, keySetId);
+                } catch (Exception e) {
+                    Log.w(TAG, "restoreKeys Exception " + e);
+                    throw e;
+                }
+            }
+        }
+
+        String getDrmPropertyString(String propertyName)
+                throws NoDrmSchemeException {
+            String v;
+            synchronized (this) {
+
+                if (mActiveDrmUUID == null && !mDrmConfigAllowed) {
+                    Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
+                    throw new NoDrmSchemeException(
+                            "getDrmPropertyString: Has to prepareDrm() first.");
+                }
+
+                try {
+                    v = mDrmObj.getPropertyString(propertyName);
+                } catch (Exception e) {
+                    Log.w(TAG, "getDrmPropertyString Exception " + e);
+                    throw e;
+                }
+            }   // synchronized
+
+            Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + v);
+
+            return v;
+        }
+
+        void setDrmPropertyString(String propertyName, String value)
+                throws NoDrmSchemeException {
+            synchronized (this) {
+
+                if ( mActiveDrmUUID == null && !mDrmConfigAllowed ) {
+                    Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
+                    throw new NoDrmSchemeException(
+                            "setDrmPropertyString: Has to prepareDrm() first.");
+                }
+
+                try {
+                    mDrmObj.setPropertyString(propertyName, value);
+                } catch ( Exception e ) {
+                    Log.w(TAG, "setDrmPropertyString Exception " + e);
+                    throw e;
+                }
+            }
+        }
+
+    }
+
+    final class SourceInfo {
         final DataSourceDesc mDSD;
         final long mId = mSrcIdGenerator.getAndIncrement();
         AtomicInteger mBufferedPercentage = new AtomicInteger(0);
+        boolean mClosed = false;
 
         // m*AsNextSource (below) only applies to pending data sources in the playlist;
         // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource}
@@ -4483,8 +4566,25 @@
         int mStateAsNextSource = NEXT_SOURCE_STATE_INIT;
         boolean mPlayPendingAsNextSource = false;
 
+        // Modular DRM
+        final DrmHandle mDrmHandle;
+        DrmInfo mDrmInfo;
+        boolean mDrmInfoResolved;
+
         SourceInfo(DataSourceDesc dsd) {
             this.mDSD = dsd;
+            mDrmHandle = new DrmHandle(dsd);
+        }
+
+        void close() {
+            synchronized (this) {
+                if (!mClosed) {
+                    if (mDSD != null) {
+                        mDSD.close();
+                    }
+                    mClosed = true;
+                }
+            }
         }
 
         @Override
@@ -4494,7 +4594,7 @@
 
     }
 
-    private SourceInfo getSourceInfoById(long srcId) {
+    private SourceInfo getSourceInfo(long srcId) {
         synchronized (mSrcLock) {
             if (isCurrentSource(srcId)) {
                 return mCurrentSourceInfo;
@@ -4506,17 +4606,68 @@
         return null;
     }
 
+    private SourceInfo getSourceInfo(DataSourceDesc dsd) {
+        synchronized (mSrcLock) {
+            if (isCurrentSource(dsd)) {
+                return mCurrentSourceInfo;
+            }
+            if (isNextSource(dsd)) {
+                return mNextSourceInfos.peek();
+            }
+        }
+        return null;
+    }
+
     private boolean isCurrentSource(long srcId) {
         synchronized (mSrcLock) {
             return mCurrentSourceInfo != null && mCurrentSourceInfo.mId == srcId;
         }
     }
 
+    private boolean isCurrentSource(DataSourceDesc dsd) {
+        synchronized (mSrcLock) {
+            return mCurrentSourceInfo != null && mCurrentSourceInfo.mDSD == dsd;
+        }
+    }
+
     private boolean isNextSource(long srcId) {
         SourceInfo nextSourceInfo = mNextSourceInfos.peek();
         return nextSourceInfo != null && nextSourceInfo.mId == srcId;
     }
 
+    private boolean isNextSource(DataSourceDesc dsd) {
+        SourceInfo nextSourceInfo = mNextSourceInfos.peek();
+        return nextSourceInfo != null && nextSourceInfo.mDSD == dsd;
+    }
+
+    @GuardedBy("mSrcLock")
+    private void setCurrentSourceInfo_l(SourceInfo sourceInfo) {
+        cleanupSourceInfo(mCurrentSourceInfo);
+        mCurrentSourceInfo = sourceInfo;
+    }
+
+    @GuardedBy("mSrcLock")
+    private void clearNextSourceInfos_l() {
+        while (!mNextSourceInfos.isEmpty()) {
+            cleanupSourceInfo(mNextSourceInfos.poll());
+        }
+    }
+
+    private void cleanupSourceInfo(SourceInfo sourceInfo) {
+        if (sourceInfo != null) {
+            sourceInfo.close();
+            Runnable task = sourceInfo.mDrmHandle.newCleanupTask();
+            mDrmThreadPool.submit(task);
+        }
+    }
+
+    private void clearSourceInfos() {
+        synchronized (mSrcLock) {
+            setCurrentSourceInfo_l(null);
+            clearNextSourceInfos_l();
+        }
+    }
+
     public static final class MetricsConstants {
         private MetricsConstants() {}
 
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3a64f43..90cfc53 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -156,7 +156,8 @@
     private static final String NOTIFICATIONS_DIR = "/notifications/";
     private static final String ALARMS_DIR = "/alarms/";
     private static final String MUSIC_DIR = "/music/";
-    private static final String PODCAST_DIR = "/podcasts/";
+    private static final String PODCASTS_DIR = "/podcasts/";
+    private static final String AUDIOBOOKS_DIR = "/audiobooks/";
 
     public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
     public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
@@ -654,7 +655,7 @@
                 // rescan for metadata if file was modified since last scan
                 if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
                     if (noMedia) {
-                        result = endFile(entry, false, false, false, false, false);
+                        result = endFile(entry, false, false, false, false, false, false);
                     } else {
                         boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
                         boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
@@ -679,11 +680,13 @@
                         boolean notifications = mScanSuccess &&
                                 (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
                         boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
-                        boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCAST_DIR) > 0);
+                        boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
+                        boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
                         boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
-                            (!ringtones && !notifications && !alarms && !podcasts));
+                            (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
 
-                        result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
+                        result = endFile(entry, ringtones, notifications, alarms, podcasts,
+                                audiobooks, music);
                     }
                 }
             } catch (RemoteException e) {
@@ -957,7 +960,7 @@
 
         @UnsupportedAppUsage
         private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
-                boolean alarms, boolean music, boolean podcasts)
+                boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
                 throws RemoteException {
             // update database
 
@@ -1003,6 +1006,7 @@
                 values.put(Audio.Media.IS_ALARM, alarms);
                 values.put(Audio.Media.IS_MUSIC, music);
                 values.put(Audio.Media.IS_PODCAST, podcasts);
+                values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
             } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
                 ExifInterface exif = null;
                 try {
@@ -1011,12 +1015,6 @@
                     // exif is null
                 }
                 if (exif != null) {
-                    float[] latlng = new float[2];
-                    if (exif.getLatLong(latlng)) {
-                        values.put(Images.Media.LATITUDE, latlng[0]);
-                        values.put(Images.Media.LONGITUDE, latlng[1]);
-                    }
-
                     long time = exif.getGpsDateTime();
                     if (time != -1) {
                         values.put(Images.Media.DATE_TAKEN, time);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index b6e3276..5e9eed7 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -20,7 +20,6 @@
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
@@ -36,7 +35,6 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
-import java.lang.ref.WeakReference;
 import java.util.List;
 
 /**
@@ -250,7 +248,7 @@
      * @throws IllegalArgumentException
      */
     public boolean sendMediaKeyEvent(KeyEvent keyEvent) throws IllegalArgumentException {
-        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+        if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
             throw new IllegalArgumentException("not a media key event");
         }
         synchronized (mInfoLock) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 436897f..874f21e 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -1029,7 +1030,7 @@
      * @throws FileNotFoundException if the provided URI could not be opened.
      * @see #getDefaultUri
      */
-    public static AssetFileDescriptor openDefaultRingtoneUri(
+    public static @Nullable AssetFileDescriptor openDefaultRingtoneUri(
             @NonNull Context context, @NonNull Uri uri) throws FileNotFoundException {
         // Try cached ringtone first since the actual provider may not be
         // encryption aware, or it may be stored on CE media storage
diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java
index e6f39e0..6a83dab 100644
--- a/media/java/android/media/UriDataSourceDesc.java
+++ b/media/java/android/media/UriDataSourceDesc.java
@@ -21,8 +21,6 @@
 import android.content.Context;
 import android.net.Uri;
 
-import com.android.internal.util.Preconditions;
-
 import java.net.CookieHandler;
 import java.net.CookieManager;
 import java.net.HttpCookie;
@@ -33,9 +31,11 @@
 
 /**
  * @hide
- * Structure for data source descriptor.
+ * Structure of data source descriptor for sources using URI.
  *
- * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)}
+ * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)},
+ * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and
+ * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}
  * to set data source for playback.
  *
  * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
@@ -158,8 +158,8 @@
          * @throws NullPointerException if context or uri is null.
          */
         public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) {
-            Preconditions.checkNotNull(context, "context cannot be null");
-            Preconditions.checkNotNull(uri, "uri cannot be null");
+            Media2Utils.checkArgument(context != null, "context cannot be null");
+            Media2Utils.checkArgument(uri != null, "uri cannot be null");
             resetDataSource();
             mUri = uri;
             mContext = context;
@@ -195,8 +195,8 @@
          */
         public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri,
                 @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies) {
-            Preconditions.checkNotNull(context, "context cannot be null");
-            Preconditions.checkNotNull(uri);
+            Media2Utils.checkArgument(context != null, "context cannot be null");
+            Media2Utils.checkArgument(uri != null, "uri cannot be null");
             if (cookies != null) {
                 CookieHandler cookieHandler = CookieHandler.getDefault();
                 if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index a7bdf4f..89a509f 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -18,11 +18,12 @@
 
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
-import android.util.Log;
-import java.lang.ref.WeakReference;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
 
 /**
  * The Visualizer class enables application to retrieve part of the currently playing audio for
@@ -455,7 +456,7 @@
      *   <li> Rfk, Ifk are respectively  the real and imaginary parts of the kth frequency
      *   component</li>
      *   <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
-     *   (k*Fs)/(n/2) </li>
+     *   k * Fs / n </li>
      * </ul>
      * <table border="0" cellspacing="0" cellpadding="0">
      * <tr><td>Index </p></td>
@@ -476,9 +477,23 @@
      *     <td>Rf2 </p></td>
      *     <td>If2 </p></td>
      *     <td>... </p></td>
-     *     <td>Rf(n-1)/2 </p></td>
-     *     <td>If(n-1)/2 </p></td></tr>
+     *     <td>Rf(n/2-1) </p></td>
+     *     <td>If(n/2-1) </p></td></tr>
      * </table>
+     * <p>In order to obtain magnitude and phase values the following code can
+     * be used:
+     *    <pre class="prettyprint">
+     *       int n = fft.size();
+     *       float[] magnitudes = new float[n / 2 + 1];
+     *       float[] phases = new float[n / 2 + 1];
+     *       magnitudes[0] = (float)Math.abs(fft[0]);      // DC
+     *       magnitudes[n / 2] = (float)Math.abs(fft[1]);  // Nyquist
+     *       phases[0] = phases[n / 2] = 0;
+     *       for (int k = 1; k &lt; n / 2; k++) {
+     *           int i = k * 2;
+     *           magnitudes[k] = (float)Math.hypot(fft[i], fft[i + 1]);
+     *           phases[k] = (float)Math.atan2(fft[i + 1], fft[i]);
+     *       }</pre>
      * @param fft array of bytes where the FFT should be returned
      * @return {@link #SUCCESS} in case of success,
      * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
@@ -561,25 +576,11 @@
          * <p>Data in the fft buffer is valid only within the scope of the callback.
          * Applications which need access to the fft data after returning from the callback
          * should make a copy of the data instead of holding a reference.
+         * <p>For the explanation of the fft data array layout, and the example
+         * code for processing it, please see the documentation for {@link #getFft(byte[])} method.
          *
-         * <p>In order to obtain magnitude and phase values the following formulas can
-         * be used:
-         *    <pre class="prettyprint">
-         *       for (int i = 0; i &lt; fft.size(); i += 2) {
-         *           float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
-         *           float phase = (float)Math.atan2(fft[i + 1], fft[i]);
-         *       }</pre>
          * @param visualizer Visualizer object on which the listener is registered.
          * @param fft array of bytes containing the frequency representation.
-         *    The fft array only contains the first half of the actual
-         *    FFT spectrum (frequencies up to Nyquist frequency), exploiting
-         *    the symmetry of the spectrum. For each frequencies bin <code>i</code>:
-         *    <ul>
-         *      <li>the element at index <code>2*i</code> in the array contains
-         *          the real part of a complex number,</li>
-         *      <li>the element at index <code>2*i+1</code> contains the imaginary
-         *          part of the complex number.</li>
-         *    </ul>
          * @param samplingRate sampling rate of the visualized audio.
          */
         void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index 511f6cd..5411e66 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -59,6 +59,8 @@
                     // read next event
                     int count = mInputStream.read(buffer);
                     if (count < 0) {
+                        // This is the exit condition as read() returning <0 indicates
+                        // that the pipe has been closed.
                         break;
                         // FIXME - inform receivers here?
                     }
@@ -81,10 +83,15 @@
                             Log.e(TAG, "Unknown packet type " + packetType);
                             break;
                     }
-                }
+                } // while (true)
             } catch (IOException e) {
                 // FIXME report I/O failure?
-                Log.e(TAG, "read failed", e);
+                // TODO: The comment above about the exit condition is not currently working
+                // as intended. The read from the closed pipe is throwing an error rather than
+                // returning <0, so this becomes (probably) not an error, but the exit case.
+                // This warrants further investigation;
+                // Silence the (probably) spurious error message.
+                // Log.e(TAG, "read failed", e);
             } finally {
                 IoUtils.closeQuietly(mInputStream);
             }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index c4b82c3..b457357 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -149,7 +149,7 @@
         if (keyEvent == null) {
             throw new IllegalArgumentException("KeyEvent may not be null");
         }
-        if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+        if (!KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
             return false;
         }
         try {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8215779..d91cf87 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,9 +96,15 @@
      * @return The binder object from the system
      * @hide
      */
+    @SystemApi
     public @NonNull ISession createSession(@NonNull MediaSession.CallbackStub cbStub,
-            @NonNull String tag, int userId) throws RemoteException {
-        return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+            @NonNull String tag, int userId) {
+        try {
+            return mService.createSession(mContext.getPackageName(), cbStub, tag, userId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null;
     }
 
     /**
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d9017b4..ff69779 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -59,9 +59,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
@@ -1400,7 +1398,7 @@
                 // ViewRootImpl always consumes the keys. In this case, the application loses
                 // a chance to handle media keys. Therefore, media keys are not dispatched to
                 // ViewRootImpl.
-                skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
+                skipDispatchToOverlayView = KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())
                         || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK;
             } else if (event instanceof MotionEvent) {
                 MotionEvent motionEvent = (MotionEvent) event;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index faf4301..e25e6a5 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -88,7 +88,7 @@
     name: "libmedia2_jni",
 
     srcs: [
-        "android_media_Media2DataSource.cpp",
+        "android_media_DataSourceCallback.cpp",
         "android_media_MediaMetricsJNI.cpp",
         "android_media_MediaPlayer2.cpp",
         "android_media_SyncParams.cpp",
diff --git a/media/jni/android_media_Media2DataSource.cpp b/media/jni/android_media_DataSourceCallback.cpp
similarity index 79%
rename from media/jni/android_media_Media2DataSource.cpp
rename to media/jni/android_media_DataSourceCallback.cpp
index b3434e9..c91d409 100644
--- a/media/jni/android_media_Media2DataSource.cpp
+++ b/media/jni/android_media_DataSourceCallback.cpp
@@ -15,10 +15,10 @@
  */
 
 //#define LOG_NDEBUG 0
-#define LOG_TAG "JMedia2DataSource-JNI"
+#define LOG_TAG "JDataSourceCallback-JNI"
 #include <utils/Log.h>
 
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
 
 #include "log/log.h"
 #include "jni.h"
@@ -33,14 +33,14 @@
 
 static const size_t kBufferSize = 64 * 1024;
 
-JMedia2DataSource::JMedia2DataSource(JNIEnv* env, jobject source)
+JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
     : mJavaObjStatus(OK),
       mSizeIsCached(false),
       mCachedSize(0) {
-    mMedia2DataSourceObj = env->NewGlobalRef(source);
-    CHECK(mMedia2DataSourceObj != NULL);
+    mDataSourceCallbackObj = env->NewGlobalRef(source);
+    CHECK(mDataSourceCallbackObj != NULL);
 
-    ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mMedia2DataSourceObj));
+    ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
     CHECK(media2DataSourceClass.get() != NULL);
 
     mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
@@ -55,17 +55,17 @@
     CHECK(mByteArrayObj != NULL);
 }
 
-JMedia2DataSource::~JMedia2DataSource() {
+JDataSourceCallback::~JDataSourceCallback() {
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    env->DeleteGlobalRef(mMedia2DataSourceObj);
+    env->DeleteGlobalRef(mDataSourceCallbackObj);
     env->DeleteGlobalRef(mByteArrayObj);
 }
 
-status_t JMedia2DataSource::initCheck() const {
+status_t JDataSourceCallback::initCheck() const {
     return OK;
 }
 
-ssize_t JMedia2DataSource::readAt(off64_t offset, void *data, size_t size) {
+ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock lock(mLock);
 
     if (mJavaObjStatus != OK) {
@@ -76,7 +76,7 @@
     }
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    jint numread = env->CallIntMethod(mMedia2DataSourceObj, mReadAtMethod,
+    jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
             (jlong)offset, mByteArrayObj, (jint)0, (jint)size);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in readAt()");
@@ -106,7 +106,7 @@
     return numread;
 }
 
-status_t JMedia2DataSource::getSize(off64_t* size) {
+status_t JDataSourceCallback::getSize(off64_t* size) {
     Mutex::Autolock lock(mLock);
 
     if (mJavaObjStatus != OK) {
@@ -118,7 +118,7 @@
     }
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    *size = env->CallLongMethod(mMedia2DataSourceObj, mGetSizeMethod);
+    *size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred in getSize()");
         jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
@@ -139,20 +139,20 @@
     return OK;
 }
 
-void JMedia2DataSource::close() {
+void JDataSourceCallback::close() {
     Mutex::Autolock lock(mLock);
 
     JNIEnv* env = JavaVMHelper::getJNIEnv();
-    env->CallVoidMethod(mMedia2DataSourceObj, mCloseMethod);
+    env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
     // The closed state is effectively the same as an error state.
     mJavaObjStatus = UNKNOWN_ERROR;
 }
 
-String8 JMedia2DataSource::toString() {
-    return String8::format("JMedia2DataSource(pid %d, uid %d)", getpid(), getuid());
+String8 JDataSourceCallback::toString() {
+    return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
 }
 
-String8 JMedia2DataSource::getMIMEType() const {
+String8 JDataSourceCallback::getMIMEType() const {
     return String8("application/octet-stream");
 }
 
diff --git a/media/jni/android_media_Media2DataSource.h b/media/jni/android_media_DataSourceCallback.h
similarity index 80%
rename from media/jni/android_media_Media2DataSource.h
rename to media/jni/android_media_DataSourceCallback.h
index dc085f3..5bde682 100644
--- a/media/jni/android_media_Media2DataSource.h
+++ b/media/jni/android_media_DataSourceCallback.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
-#define _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#ifndef _ANDROID_MEDIA_DATASOURCECALLBACK_H_
+#define _ANDROID_MEDIA_DATASOURCECALLBACK_H_
 
 #include "jni.h"
 
@@ -26,16 +26,16 @@
 
 namespace android {
 
-// The native counterpart to a Java android.media.Media2DataSource. It inherits from
+// The native counterpart to a Java android.media.DataSourceCallback. It inherits from
 // DataSource.
 //
 // If the java DataSource returns an error or throws an exception it
 // will be considered to be in a broken state, and the only further call this
 // will make is to close().
-class JMedia2DataSource : public DataSource {
+class JDataSourceCallback : public DataSource {
 public:
-    JMedia2DataSource(JNIEnv *env, jobject source);
-    virtual ~JMedia2DataSource();
+    JDataSourceCallback(JNIEnv *env, jobject source);
+    virtual ~JDataSourceCallback();
 
     virtual status_t initCheck() const override;
     virtual ssize_t readAt(off64_t offset, void *data, size_t size) override;
@@ -56,15 +56,15 @@
     bool mSizeIsCached;
     off64_t mCachedSize;
 
-    jobject mMedia2DataSourceObj;
+    jobject mDataSourceCallbackObj;
     jmethodID mReadAtMethod;
     jmethodID mGetSizeMethod;
     jmethodID mCloseMethod;
     jbyteArray mByteArrayObj;
 
-    DISALLOW_EVIL_CONSTRUCTORS(JMedia2DataSource);
+    DISALLOW_EVIL_CONSTRUCTORS(JDataSourceCallback);
 };
 
 }  // namespace android
 
-#endif  // _ANDROID_MEDIA_MEDIA2DATASOURCE_H_
+#endif  // _ANDROID_MEDIA_DATASOURCECALLBACK_H_
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index f7de2e7..4567492 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -46,7 +46,7 @@
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
 #include "android_media_BufferingParams.h"
-#include "android_media_Media2DataSource.h"
+#include "android_media_DataSourceCallback.h"
 #include "android_media_MediaMetricsJNI.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
@@ -423,7 +423,7 @@
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
         return;
     }
-    sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
+    sp<DataSource> callbackDataSource = new JDataSourceCallback(env, dataSource);
     sp<DataSourceDesc> dsd = new DataSourceDesc();
     dsd->mId = srcId;
     dsd->mType = DataSourceDesc::TYPE_CALLBACK;
@@ -1390,7 +1390,7 @@
     },
     {
         "nativeHandleDataSourceCallback",
-        "(ZJLandroid/media/Media2DataSource;JJ)V",
+        "(ZJLandroid/media/DataSourceCallback;JJ)V",
         (void *)android_media_MediaPlayer2_handleDataSourceCallback
     },
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
diff --git a/media/proto/Android.bp b/media/proto/Android.bp
index 50d44c3..74fd525 100644
--- a/media/proto/Android.bp
+++ b/media/proto/Android.bp
@@ -7,6 +7,7 @@
     srcs: ["mediaplayer2.proto"],
     no_framework_libs: true,
     jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "28",
 }
 
 cc_library_static {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
new file mode 100644
index 0000000..f578e46
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/SurfaceUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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.mediaframeworktest.unit;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.ImageReader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Surface;
+
+import junit.framework.Assert;
+
+public class SurfaceUtilsTest extends junit.framework.TestCase {
+
+    @SmallTest
+    public void testInvalidSurfaceException() {
+        ImageReader reader = ImageReader.newInstance(640, 480, ImageFormat.YUV_420_888, 1);
+        Surface surface = reader.getSurface();
+        surface.release();
+
+        try {
+            SurfaceUtils.isFlexibleConsumer(surface);
+            Assert.fail("unreachable");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+
+        reader.close();
+    }
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index e7e8384..207508e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -144,6 +144,7 @@
     AHardwareBuffer_describe; # introduced=26
     AHardwareBuffer_fromHardwareBuffer; # introduced=26
     AHardwareBuffer_getNativeHandle; # introduced=26
+    AHardwareBuffer_isSupported; # introduced=29
     AHardwareBuffer_lock; # introduced=26
     AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26
     AHardwareBuffer_release; # introduced=26
diff --git a/native/webview/plat_support/draw_gl.h b/native/webview/plat_support/draw_gl.h
index c8434b6..de13ed0 100644
--- a/native/webview/plat_support/draw_gl.h
+++ b/native/webview/plat_support/draw_gl.h
@@ -43,9 +43,9 @@
   // Input: tells the draw function what action to perform.
   enum Mode {
     kModeDraw = 0,
-    kModeProcess,
-    kModeProcessNoContext,
-    kModeSync,
+    kModeProcess = 1,
+    kModeProcessNoContext = 2,
+    kModeSync = 3,
   } mode;
 
   // Input: current clip rect in surface coordinates. Reflects the current state
@@ -93,9 +93,9 @@
                                 AwDrawGLInfo* draw_info,
                                 void* spare);
 enum AwMapMode {
-  MAP_READ_ONLY,
-  MAP_WRITE_ONLY,
-  MAP_READ_WRITE,
+  MAP_READ_ONLY = 0,
+  MAP_WRITE_ONLY = 1,
+  MAP_READ_WRITE = 2,
 };
 
 // Called to create a GraphicBuffer
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_down.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_in_rotate_up.xml
diff --git a/packages/SystemUI/res/anim/car_arrow_fade_out.xml b/packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
similarity index 100%
rename from packages/SystemUI/res/anim/car_arrow_fade_out.xml
rename to packages/CarSystemUI/res/anim/car_arrow_fade_out.xml
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
index ed637a7..6f12338 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
index 227c981..9f8c12e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
index 5901ff4..adc1f72 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
index 41cbe4b..dec5c05 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
index 341e7e0..986a9cb 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_close_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
index 6ae7413..80b38b3 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
index 06ac9e3..721376c 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_icon_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
index 4baefb8..246099e 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_name_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
similarity index 93%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
index 2d0deb9..9a1c642 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pages_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
similarity index 95%
rename from packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
rename to packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
index 3315220..1414b66 100644
--- a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
+++ b/packages/CarSystemUI/res/anim/car_user_switcher_open_pod_animation.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/drawable/car_add_circle_round.xml b/packages/CarSystemUI/res/drawable/car_add_circle_round.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_add_circle_round.xml
rename to packages/CarSystemUI/res/drawable/car_add_circle_round.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_add_white.xml b/packages/CarSystemUI/res/drawable/car_ic_add_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_add_white.xml
rename to packages/CarSystemUI/res/drawable/car_ic_add_white.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
similarity index 94%
rename from packages/SystemUI/res/drawable/car_ic_arrow.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow.xml
index d400ed8..cfacbf9 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
similarity index 93%
rename from packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
rename to packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
index 33a512e..81e7262 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_arrow_drop_up.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2015 The Android Open Source Project
+  ~ Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/res/drawable/car_ic_hvac.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_hvac.xml
rename to packages/CarSystemUI/res/drawable/car_ic_hvac.xml
diff --git a/packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml b/packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
rename to packages/CarSystemUI/res/drawable/car_ic_keyboard_arrow_down.xml
diff --git a/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
new file mode 100644
index 0000000..eb501e5
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_rounded_bg_bottom.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners
+        android:bottomLeftRadius="@dimen/car_radius_3"
+        android:topLeftRadius="0dp"
+        android:bottomRightRadius="@dimen/car_radius_3"
+        android:topRightRadius="0dp"
+        />
+</shape>
diff --git a/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
new file mode 100644
index 0000000..34578fe
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_stat_sys_data_bluetooth_indicator.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2018 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="17dp"
+        android:height="17dp"
+        android:viewportWidth="18.0"
+        android:viewportHeight="18.0">
+    <group
+        android:translateY="0.5"
+        android:translateX="0.5" >
+        <path
+            android:pathData="M9.57,8.5l2.79,-2.78c0.3,-0.3 0.3,-0.8 0,-1.1L9.04,1.29L9.02,1.27C8.7,0.98 8.21,1 7.91,1.31C7.78,1.45 7.71,1.64 7.71,1.84v4.79L4.69,3.61c-0.3,-0.3 -0.79,-0.3 -1.09,0s-0.3,0.79 0,1.09L7.39,8.5L3.6,12.29c-0.3,0.3 -0.3,0.79 0,1.09s0.79,0.3 1.09,0l3.01,-3.01v4.8c0,0.42 0.35,0.77 0.77,0.77c0.19,0 0.39,-0.07 0.53,-0.21l0.04,-0.04l3.32,-3.32c0.3,-0.3 0.3,-0.8 0,-1.1L9.57,8.5zM9.19,6.77v-3.2l1.6,1.6L9.19,6.77zM9.19,13.42v-3.2l1.6,1.6L9.19,13.42zM4.03,9.29c-0.44,0.44 -1.15,0.44 -1.58,0C2.02,8.86 2.02,8.16 2.45,7.72l0.01,-0.01C2.89,7.27 3.59,7.27 4.02,7.7l0.01,0.01C4.47,8.15 4.47,8.85 4.03,9.29zM14.44,7.71c0.44,0.44 0.44,1.15 0,1.58c-0.44,0.44 -1.15,0.44 -1.58,0c-0.44,-0.43 -0.44,-1.13 -0.01,-1.57l0.01,-0.01C13.3,7.28 14,7.27 14.43,7.7C14.44,7.7 14.44,7.71 14.44,7.71z"
+            android:fillColor="#FFFFFF"/>
+    </group>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/ic_mic_white.xml b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
index f5a91b5..e1e389d 100644
--- a/packages/CarSystemUI/res/drawable/ic_mic_white.xml
+++ b/packages/CarSystemUI/res/drawable/ic_mic_white.xml
@@ -1,3 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="48dp"
         android:height="48dp"
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
similarity index 97%
rename from packages/SystemUI/res/layout/car_facet_button.xml
rename to packages/CarSystemUI/res/layout/car_facet_button.xml
index ad86049..8e7ebad 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/CarSystemUI/res/layout/car_facet_button.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2017, The Android Open Source Project
+** Copyright 2018, 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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
index ee8d357..1d67286 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
rename to packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
index c9f5148..6cd70d6 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/CarSystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_left_navigation_bar.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
similarity index 95%
rename from packages/SystemUI/res/layout/car_navigation_button.xml
rename to packages/CarSystemUI/res/layout/car_navigation_button.xml
index 4062eb8..6d8cca9 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, 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.
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/CarSystemUI/res/layout/car_qs_footer.xml
similarity index 98%
rename from packages/SystemUI/res/layout/car_qs_footer.xml
rename to packages/CarSystemUI/res/layout/car_qs_footer.xml
index 3afd4ea..6f19cfc 100644
--- a/packages/SystemUI/res/layout/car_qs_footer.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_footer.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/CarSystemUI/res/layout/car_qs_panel.xml
similarity index 96%
rename from packages/SystemUI/res/layout/car_qs_panel.xml
rename to packages/CarSystemUI/res/layout/car_qs_panel.xml
index e7413de..dfa48c3 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/CarSystemUI/res/layout/car_qs_panel.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 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.
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
similarity index 98%
copy from packages/SystemUI/res/layout/car_left_navigation_bar.xml
copy to packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index 02be457..141b28a 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 **
-** Copyright 2016, The Android Open Source Project
+** Copyright 2018, 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.
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
similarity index 100%
rename from packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
rename to packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
diff --git a/packages/SystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/colors_car.xml
rename to packages/CarSystemUI/res/values/colors_car.xml
index 49bfb25..2f720f5 100644
--- a/packages/SystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
- * Copyright 2017, The Android Open Source Project
+ * Copyright 2018, 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.
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
similarity index 97%
rename from packages/SystemUI/res/values/dimens_car.xml
rename to packages/CarSystemUI/res/values/dimens_car.xml
index afbe176..c027f81 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
- * Copyright (c) 2016, The Android Open Source Project
+ * Copyright (c) 2018, 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.
diff --git a/packages/SystemUI/res/values/ids_car.xml b/packages/CarSystemUI/res/values/ids_car.xml
similarity index 100%
rename from packages/SystemUI/res/values/ids_car.xml
rename to packages/CarSystemUI/res/values/ids_car.xml
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
similarity index 95%
rename from packages/SystemUI/res/values/integers_car.xml
rename to packages/CarSystemUI/res/values/integers_car.xml
index fc3623c..472c957 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  Copyright (c) 2017, The Android Open Source Project
+  Copyright (c) 2018, 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.
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
similarity index 96%
rename from packages/SystemUI/res/values/strings_car.xml
rename to packages/CarSystemUI/res/values/strings_car.xml
index 2890cf2..83e91c5 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /**
- * Copyright (c) 2016, The Android Open Source Project
+ * Copyright (c) 2018, 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.
diff --git a/packages/CarSystemUI/res/xml/car_volume_items.xml b/packages/CarSystemUI/res/xml/car_volume_items.xml
index 8715946..922b1a7 100644
--- a/packages/CarSystemUI/res/xml/car_volume_items.xml
+++ b/packages/CarSystemUI/res/xml/car_volume_items.xml
@@ -23,8 +23,8 @@
         car:icon="@drawable/car_ic_music"/>
   <item car:usage="media"
         car:icon="@drawable/car_ic_music"/>
-  <item car:usage="voice_communication"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
+  <item car:usage="assistance_navigation_guidance"
+        car:icon="@drawable/car_ic_navigation"/>
   <item car:usage="voice_communication_signalling"
         car:icon="@*android:drawable/ic_audio_ring_notif"/>
   <item car:usage="alarm"
@@ -43,8 +43,8 @@
         car:icon="@drawable/car_ic_notification"/>
   <item car:usage="assistance_accessibility"
         car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="assistance_navigation_guidance"
-        car:icon="@drawable/car_ic_navigation"/>
+  <item car:usage="voice_communication"
+        car:icon="@*android:drawable/ic_audio_ring_notif"/>
   <item car:usage="assistance_sonification"
         car:icon="@drawable/car_ic_notification"/>
   <item car:usage="game"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index dfe5704..f57f26d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -28,6 +28,8 @@
 import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.volume.CarVolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
 
 /**
  * Class factory to provide car specific SystemUI components.
@@ -39,6 +41,10 @@
         return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
     }
 
+    public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+        return new CarVolumeDialogComponent(systemUi, context);
+    }
+
     @Override
     public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
         Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
rename to packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
index 50fefe9..0563418 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationEntryManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -11,8 +11,9 @@
  * 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
+ * limitations under the License.
  */
+
 package com.android.systemui.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 0389030..b74f199 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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
+ * 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.
+ * 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.qs.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 3e82c54..41c37d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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
+ * 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.
+ * 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.qs.car;
 
 import android.animation.Animator;
@@ -223,7 +226,7 @@
 
     private void animateHeightChange(boolean opening) {
         // Animation in progress; cancel it to avoid contention.
-        if (mAnimatorSet != null){
+        if (mAnimatorSet != null) {
             mAnimatorSet.cancel();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
rename to packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 083a747..d5dd3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/CarSystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -1,16 +1,19 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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
+ * 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.
+ * 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.qs.car;
 
 import android.content.Context;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
new file mode 100644
index 0000000..5bf30ca
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/AssitantButton.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.car;
+
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+
+/**
+ * AssitantButton is a ui component that will trigger the Voice Interaction Service.
+ */
+public class AssitantButton extends CarFacetButton {
+
+    private static final String TAG = "CarFacetButton";
+    private IVoiceInteractionSessionShowCallback mShowCallback =
+            new IVoiceInteractionSessionShowCallback.Stub() {
+                @Override
+                public void onFailed() {
+                    Log.w(TAG, "Failed to show VoiceInteractionSession");
+                }
+
+                @Override
+                public void onShown() {
+                    Log.d(TAG, "IVoiceInteractionSessionShowCallback onShown()");
+                }
+            };
+
+    private static final String EXTRA_CAR_PUSH_TO_TALK =
+            "com.android.car.input.EXTRA_CAR_PUSH_TO_TALK";
+    private final AssistUtils mAssistUtils;
+
+    public AssitantButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mAssistUtils = new AssistUtils(context);
+        setOnClickListener(v -> {
+            showAssistant();
+        });
+    }
+
+    private void showAssistant() {
+        final Bundle args = new Bundle();
+        args.putBoolean(EXTRA_CAR_PUSH_TO_TALK, true);
+        mAssistUtils.showSessionForActiveService(args,
+                SHOW_SOURCE_ASSIST_GESTURE, mShowCallback, /*activityToken=*/ null);
+    }
+
+    @Override
+    protected void setupIntents(TypedArray typedArray){
+        // left blank because for the assistant button Intent will not be passed from the layout.
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index fc39648..58f80a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -58,28 +58,31 @@
     private final Context mContext;
 
     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
-    private BluetoothHeadsetClient mBluetoothHeadsetClient;
-
     private final ArrayList<BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
+    private BluetoothHeadsetClient mBluetoothHeadsetClient;
+    private final ServiceListener mHfpServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+            }
+        }
 
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = null;
+            }
+        }
+    };
     private int mLevel;
-
-    /**
-     * An interface indicating the container of a View that will display what the information
-     * in the {@link CarBatteryController}.
-     */
-    public interface BatteryViewHandler {
-        void hideBatteryView();
-        void showBatteryView();
-    }
-
     private BatteryViewHandler mBatteryViewHandler;
 
     public CarBatteryController(Context context) {
         mContext = context;
 
         if (mAdapter == null) {
-           return;
+            return;
         }
 
         mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -159,7 +162,7 @@
 
             }
             BluetoothDevice device =
-                    (BluetoothDevice)intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
+                    (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
             updateBatteryIcon(device, newState);
         }
     }
@@ -261,20 +264,14 @@
         }
     }
 
-    private final ServiceListener mHfpServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
-            }
-        }
+    /**
+     * An interface indicating the container of a View that will display what the information
+     * in the {@link CarBatteryController}.
+     */
+    public interface BatteryViewHandler {
+        void hideBatteryView();
 
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = null;
-            }
-        }
-    };
+        void showBatteryView();
+    }
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index bd32856..cea4ab0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.car;
 
 import android.content.Context;
@@ -31,6 +47,7 @@
     private static final String EXTRA_FACET_PACKAGES = "packages";
     private static final String EXTRA_FACET_ID = "filter_id";
     private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+    private static final String TAG = "CarFacetButton";
 
     private Context mContext;
     private AlphaOptimizedImageButton mIcon;
@@ -51,12 +68,10 @@
     private float mSelectedAlpha = 1f;
     private float mUnselectedAlpha = 1f;
 
-
     public CarFacetButton(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
         View.inflate(context, R.layout.car_facet_button, this);
-
         // extract custom attributes
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
         setupIntents(typedArray);
@@ -64,13 +79,12 @@
         CarFacetButtonController carFacetButtonController = Dependency.get(
                 CarFacetButtonController.class);
         carFacetButtonController.addFacetButton(this);
-
     }
 
     /**
      * Reads the custom attributes to setup click handlers for this component.
      */
-    private void setupIntents(TypedArray typedArray) {
+    protected void setupIntents(TypedArray typedArray) {
         String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
         String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
         String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
@@ -111,7 +125,6 @@
         }
     }
 
-
     private void setupIcons(TypedArray styledAttributes) {
         mSelectedAlpha = styledAttributes.getFloat(
                 R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
@@ -153,6 +166,9 @@
         return mFacetPackages;
     }
 
+    /**
+     * @return The list of component names.
+     */
     public String[] getComponentName() {
         if (mComponentNames == null) {
             return new String[0];
@@ -162,6 +178,7 @@
 
     /**
      * Updates the alpha of the icons to "selected" and shows the "More icon"
+     *
      * @param selected true if the view must be selected, false otherwise
      */
     public void setSelected(boolean selected) {
@@ -171,7 +188,8 @@
 
     /**
      * Updates the visual state to let the user know if it's been selected.
-     * @param selected true if should update the alpha of the icon to selected, false otherwise
+     *
+     * @param selected     true if should update the alpha of the icon to selected, false otherwise
      * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
      *                     is ignored if the attribute useMoreIcon is set to false
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index 20986ad..56db242 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.car;
 
 import android.app.ActivityManager;
@@ -34,7 +50,6 @@
      * Add facet button to this controller. The expected use is for the facet button
      * to get a reference to this controller via {@link com.android.systemui.Dependency}
      * and self add.
-     * @param facetButton
      */
     public void addFacetButton(CarFacetButton facetButton) {
         String[] categories = facetButton.getCategories();
@@ -70,15 +85,16 @@
      * They will then be compared with the supplied StackInfo list.
      * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
      * for consideration if it has the same displayId as the CarFacetButtons.
-     * @param taskInfo of the currently running application
+     *
+     * @param stackInfoList of the currently running application
      */
     public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
         int displayId = getDisplayId();
         ActivityManager.StackInfo validStackInfo = null;
-        for (ActivityManager.StackInfo stackInfo :stackInfoList) {
+        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
             // If the display id is unknown or it matches the stack, it's valid for use
-            if ((displayId == -1 || displayId == stackInfo.displayId) &&
-                    stackInfo.topActivity != null) {
+            if ((displayId == -1 || displayId == stackInfo.displayId)
+                    && stackInfo.topActivity != null) {
                 validStackInfo = stackInfo;
                 break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 084c136..e640baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.car;
 
 import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 4bff5ba..2d90f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -35,6 +35,8 @@
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.statusbar.StatusBarState;
@@ -252,6 +254,11 @@
         addTemperatureViewToController(mStatusBarWindow);
     }
 
+    @Override
+    protected QS createDefaultQSFragment() {
+        return new CarQSFragment();
+    }
+
     private BatteryController createBatteryController() {
         mCarBatteryController = new CarBatteryController(mContext);
         mCarBatteryController.addBatteryViewHandler(this);
@@ -549,7 +556,7 @@
      */
     public void dismissKeyguard() {
         executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
-            true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+                true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
index d0f0629..8c6b9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.car;
 
 import android.content.Context;
@@ -16,13 +32,13 @@
             ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils) {
         super(context, callback, lockPatternUtils);
-        mShouldHideNavBar =context.getResources()
+        mShouldHideNavBar = context.getResources()
                 .getBoolean(R.bool.config_hideNavWhenKeyguardBouncerShown);
     }
 
     @Override
     protected void updateNavigationBarVisibility(boolean navBarVisible) {
-        if(!mShouldHideNavBar) {
+        if (!mShouldHideNavBar) {
             return;
         }
         CarStatusBar statusBar = (CarStatusBar) mStatusBar;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
index f2923f7..3288927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.car;
 
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
@@ -29,7 +45,7 @@
  */
 public class ConnectedDeviceSignalController extends BroadcastReceiver implements
         BluetoothController.Callback {
-    private final static String TAG = "DeviceSignalCtlr";
+    private static final String TAG = "DeviceSignalCtlr";
 
     /**
      * The value that indicates if a network is unavailable. This value is according ot the
@@ -70,6 +86,21 @@
     private final SignalDrawable mSignalDrawable;
 
     private BluetoothHeadsetClient mBluetoothHeadsetClient;
+    private final ServiceListener mHfpServiceListener = new ServiceListener() {
+        @Override
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(int profile) {
+            if (profile == BluetoothProfile.HEADSET_CLIENT) {
+                mBluetoothHeadsetClient = null;
+            }
+        }
+    };
 
     public ConnectedDeviceSignalController(Context context, View signalsView) {
         mContext = context;
@@ -87,7 +118,7 @@
                 new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
 
         if (mAdapter == null) {
-          return;
+            return;
         }
 
         mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
@@ -236,20 +267,4 @@
             mSignalsView.setVisibility(View.GONE);
         }
     }
-
-    private final ServiceListener mHfpServiceListener = new ServiceListener() {
-        @Override
-        public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(int profile) {
-            if (profile == BluetoothProfile.HEADSET_CLIENT) {
-                mBluetoothHeadsetClient = null;
-            }
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
index 47941bf..730c3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
@@ -124,4 +124,4 @@
             Log.d(TAG, message);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 2ebf5eb..23fe594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.car;
@@ -44,7 +44,7 @@
         // Initialize user grid.
         mUserGridView = container.findViewById(R.id.user_grid);
         GridLayoutManager layoutManager = new GridLayoutManager(context,
-            context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+                context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
         mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
@@ -54,7 +54,7 @@
         hide();
 
         mShortAnimDuration = container.getResources()
-            .getInteger(android.R.integer.config_shortAnimTime);
+                .getInteger(android.R.integer.config_shortAnimTime);
     }
 
     /**
@@ -108,4 +108,4 @@
                 });
 
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/SwitchToGuestTimer.java
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index d802ed8..fb2b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.car;
@@ -210,7 +210,7 @@
         public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
             UserRecord userRecord = mUsers.get(position);
             RoundedBitmapDrawable circleIcon = RoundedBitmapDrawableFactory.create(mRes,
-                getUserRecordIcon(userRecord));
+                    getUserRecordIcon(userRecord));
             circleIcon.setCircular(true);
             holder.mUserAvatarImageView.setImageDrawable(circleIcon);
             holder.mUserNameTextView.setText(userRecord.mInfo.name);
@@ -254,13 +254,13 @@
 
         private void showMaxUserLimitReachedDialog() {
             AlertDialog maxUsersDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
-                .setTitle(R.string.user_limit_reached_title)
-                .setMessage(getResources().getQuantityString(
-                    R.plurals.user_limit_reached_message,
-                    mCarUserManagerHelper.getMaxSupportedRealUsers(),
-                    mCarUserManagerHelper.getMaxSupportedRealUsers()))
-                .setPositiveButton(android.R.string.ok, null)
-                .create();
+                    .setTitle(R.string.user_limit_reached_title)
+                    .setMessage(getResources().getQuantityString(
+                            R.plurals.user_limit_reached_message,
+                            mCarUserManagerHelper.getMaxSupportedRealUsers(),
+                            mCarUserManagerHelper.getMaxSupportedRealUsers()))
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
             // Sets window flags for the SysUI dialog
             SystemUIDialog.applyFlags(maxUsersDialog);
             maxUsersDialog.show();
@@ -268,17 +268,17 @@
 
         private void showConfirmAddUserDialog() {
             String message = mRes.getString(R.string.user_add_user_message_setup)
-                .concat(System.getProperty("line.separator"))
-                .concat(System.getProperty("line.separator"))
-                .concat(mRes.getString(R.string.user_add_user_message_update));
+                    .concat(System.getProperty("line.separator"))
+                    .concat(System.getProperty("line.separator"))
+                    .concat(mRes.getString(R.string.user_add_user_message_update));
 
             AlertDialog addUserDialog = new Builder(mContext, R.style.Theme_Car_Dark_Dialog_Alert)
-                .setTitle(R.string.user_add_user_title)
-                .setMessage(message)
-                .setNegativeButton(android.R.string.cancel, this)
-                .setPositiveButton(android.R.string.ok, this)
-                .setOnCancelListener(this)
-                .create();
+                    .setTitle(R.string.user_add_user_title)
+                    .setMessage(message)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setOnCancelListener(this)
+                    .create();
             // Sets window flags for the SysUI dialog
             SystemUIDialog.applyFlags(addUserDialog);
             addUserDialog.show();
@@ -298,7 +298,7 @@
 
             if (userRecord.mIsAddUser) {
                 return UserIcons.convertToBitmap(mContext
-                    .getDrawable(R.drawable.car_add_circle_round));
+                        .getDrawable(R.drawable.car_add_circle_round));
             }
 
             return mCarUserManagerHelper.getUserIcon(userRecord.mInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
index 6c924e3..aec31ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -41,117 +41,13 @@
 public class HvacController {
 
     public static final String TAG = "HvacController";
-    public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+    public static final int BIND_TO_HVAC_RETRY_DELAY = 5000;
 
     private Context mContext;
     private Handler mHandler;
     private Car mCar;
     private CarHvacManager mHvacManager;
     private HashMap<HvacKey, List<TemperatureView>> mTempComponents = new HashMap<>();
-
-    public HvacController(Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Create connection to the Car service. Note: call backs from the Car service
-     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
-     */
-    public void connectToCarService() {
-        mHandler = new Handler();
-        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
-        if (mCar != null) {
-            // note: this connect call handles the retries
-            mCar.connect();
-        }
-    }
-
-    /**
-     * Registers callbacks and initializes components upon connection.
-     */
-    private ServiceConnection mServiceConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                service.linkToDeath(mRestart, 0);
-                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
-                mHvacManager.registerCallback(mHardwareCallback);
-                initComponents();
-            } catch (Exception e) {
-                Log.e(TAG, "Failed to correctly connect to HVAC", e);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            destroyHvacManager();
-        }
-    };
-
-    private void destroyHvacManager() {
-        if (mHvacManager != null) {
-            mHvacManager.unregisterCallback(mHardwareCallback);
-            mHvacManager = null;
-        }
-    }
-
-    /**
-     * If the connection to car service goes away then restart it.
-     */
-    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            Log.d(TAG, "Death of HVAC triggering a restart");
-            if (mCar != null) {
-                mCar.disconnect();
-            }
-            destroyHvacManager();
-            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
-        }
-    };
-
-    /**
-     * Add component to list and initialize it if the connection is up.
-     * @param temperatureView
-     */
-    public void addHvacTextView(TemperatureView temperatureView) {
-
-        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
-        if (!mTempComponents.containsKey(hvacKey)) {
-            mTempComponents.put(hvacKey, new ArrayList<>());
-        }
-        mTempComponents.get(hvacKey).add(temperatureView);
-        initComponent(temperatureView);
-    }
-
-    private void initComponents() {
-        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
-                mTempComponents.entrySet().iterator();
-        while (iterator.hasNext()) {
-            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
-            List<TemperatureView> temperatureViews = next.getValue();
-            for (TemperatureView view : temperatureViews) {
-                initComponent(view);
-            }
-        }
-    }
-
-
-    private void initComponent(TemperatureView view) {
-        int id = view.getPropertyId();
-        int zone = view.getAreaId();
-        try {
-            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
-                view.setTemp(Float.NaN);
-                return;
-            }
-            view.setTemp(mHvacManager.getFloatProperty(id, zone));
-        } catch (Exception e) {
-            view.setTemp(Float.NaN);
-            Log.e(TAG, "Failed to get value from hvac service", e);
-        }
-    }
-
     /**
      * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
      * match.
@@ -179,10 +75,109 @@
 
         @Override
         public void onErrorEvent(final int propertyId, final int zone) {
-            Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
-                    " zone: " + zone);
+            Log.d(TAG, "HVAC error event, propertyId: " + propertyId
+                    + " zone: " + zone);
         }
     };
+    /**
+     * If the connection to car service goes away then restart it.
+     */
+    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Death of HVAC triggering a restart");
+            if (mCar != null) {
+                mCar.disconnect();
+            }
+            destroyHvacManager();
+            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+        }
+    };
+    /**
+     * Registers callbacks and initializes components upon connection.
+     */
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                service.linkToDeath(mRestart, 0);
+                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+                mHvacManager.registerCallback(mHardwareCallback);
+                initComponents();
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to correctly connect to HVAC", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            destroyHvacManager();
+        }
+    };
+
+    public HvacController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Create connection to the Car service. Note: call backs from the Car service
+     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+     */
+    public void connectToCarService() {
+        mHandler = new Handler();
+        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+        if (mCar != null) {
+            // note: this connect call handles the retries
+            mCar.connect();
+        }
+    }
+
+    private void destroyHvacManager() {
+        if (mHvacManager != null) {
+            mHvacManager.unregisterCallback(mHardwareCallback);
+            mHvacManager = null;
+        }
+    }
+
+    /**
+     * Add component to list and initialize it if the connection is up.
+     */
+    public void addHvacTextView(TemperatureView temperatureView) {
+
+        HvacKey hvacKey = new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId());
+        if (!mTempComponents.containsKey(hvacKey)) {
+            mTempComponents.put(hvacKey, new ArrayList<>());
+        }
+        mTempComponents.get(hvacKey).add(temperatureView);
+        initComponent(temperatureView);
+    }
+
+    private void initComponents() {
+        Iterator<Map.Entry<HvacKey, List<TemperatureView>>> iterator =
+                mTempComponents.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<HvacKey, List<TemperatureView>> next = iterator.next();
+            List<TemperatureView> temperatureViews = next.getValue();
+            for (TemperatureView view : temperatureViews) {
+                initComponent(view);
+            }
+        }
+    }
+
+    private void initComponent(TemperatureView view) {
+        int id = view.getPropertyId();
+        int zone = view.getAreaId();
+        try {
+            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+                view.setTemp(Float.NaN);
+                return;
+            }
+            view.setTemp(mHvacManager.getFloatProperty(id, zone));
+        } catch (Exception e) {
+            view.setTemp(Float.NaN);
+            Log.e(TAG, "Failed to get value from hvac service", e);
+        }
+    }
 
     /**
      * Removes all registered components. This is useful if you need to rebuild the UI since
@@ -200,7 +195,7 @@
         int mPropertyId;
         int mAreaId;
 
-        public HvacKey(int propertyId, int areaId) {
+        private HvacKey(int propertyId, int areaId) {
             mPropertyId = propertyId;
             mAreaId = areaId;
         }
@@ -210,8 +205,8 @@
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             HvacKey hvacKey = (HvacKey) o;
-            return mPropertyId == hvacKey.mPropertyId &&
-                    mAreaId == hvacKey.mAreaId;
+            return mPropertyId == hvacKey.mPropertyId
+                    && mAreaId == hvacKey.mAreaId;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
index 4d8ce43..507c60f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureTextView.java
@@ -40,7 +40,7 @@
     public TemperatureTextView(Context context, AttributeSet attrs) {
         super(context, attrs);
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
-        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId, -1);
         mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
         String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
         mTempFormat = (format == null) ? "%.1f\u00B0" : format;
@@ -48,6 +48,7 @@
 
     /**
      * Formats the float for display
+     *
      * @param temp - The current temp or NaN
      */
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
new file mode 100644
index 0000000..71cc19b
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.volume;
+
+import android.content.Context;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.VolumeDialog;
+
+/**
+ * Allows for adding car specific dialog when the volume dialog is created.
+ */
+public class CarVolumeDialogComponent extends VolumeDialogComponent {
+
+    public CarVolumeDialogComponent(SystemUI sysui, Context context) {
+        super(sysui, context);
+    }
+
+    protected VolumeDialog createDefault() {
+        return new CarVolumeDialogImpl(mContext);
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
new file mode 100644
index 0000000..12df263
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2018 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.volume;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.media.CarAudioManager;
+import android.car.media.ICarVolumeCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.ServiceConnection;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemAdapter.BackgroundStyle;
+import androidx.car.widget.ListItemProvider.ListProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.SeekbarListItem;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.VolumeDialog;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Car version of the volume dialog.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class CarVolumeDialogImpl implements VolumeDialog {
+
+    private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
+
+    private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
+    private static final String XML_TAG_VOLUME_ITEM = "item";
+    private static final int HOVERING_TIMEOUT = 16000;
+    private static final int NORMAL_TIMEOUT = 3000;
+    private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
+    private static final int DISMISS_DELAY_IN_MILLIS = 50;
+    private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
+
+    private final Context mContext;
+    private final H mHandler = new H();
+    // All the volume items.
+    private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
+    // Available volume items in car audio manager.
+    private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
+    // Volume items in the PagedListView.
+    private final List<ListItem> mVolumeLineItems = new ArrayList<>();
+    private final KeyguardManager mKeyguard;
+    private Window mWindow;
+    private CustomDialog mDialog;
+    private PagedListView mListView;
+    private ListItemAdapter mPagedListAdapter;
+    private Car mCar;
+    private CarAudioManager mCarAudioManager;
+    private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
+        @Override
+        public void onGroupVolumeChanged(int groupId, int flags) {
+            VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+            int value = getSeekbarValue(mCarAudioManager, groupId);
+            // Do not update the progress if it is the same as before. When car audio manager sets
+            // its group volume caused by the seekbar progress changed, it also triggers this
+            // callback. Updating the seekbar at the same time could block the continuous seeking.
+            if (value != volumeItem.progress) {
+                volumeItem.listItem.setProgress(value);
+                volumeItem.progress = value;
+            }
+            if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+                mHandler.obtainMessage(H.SHOW, Events.SHOW_REASON_VOLUME_CHANGED).sendToTarget();
+            }
+        }
+
+        @Override
+        public void onMasterMuteChanged(int flags) {
+            // ignored
+        }
+    };
+    private boolean mHovering;
+    private boolean mShowing;
+    private boolean mExpanded;
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mExpanded = false;
+                mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
+                int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
+                // Populates volume slider items from volume groups to UI.
+                for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
+                    VolumeItem volumeItem = getVolumeItemForUsages(
+                            mCarAudioManager.getUsagesForVolumeGroupId(groupId));
+                    mAvailableVolumeItems.add(volumeItem);
+                    // The first one is the default item.
+                    if (groupId == 0) {
+                        volumeItem.defaultItem = true;
+                        addSeekbarListItem(volumeItem, groupId,
+                                R.drawable.car_ic_keyboard_arrow_down,
+                                new ExpandIconListener());
+                    }
+                }
+
+                // If list is already initiated, update its content.
+                if (mPagedListAdapter != null) {
+                    mPagedListAdapter.notifyDataSetChanged();
+                }
+                mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car is not connected!", e);
+            }
+        }
+
+        /**
+         * This does not get called when service is properly disconnected.
+         * So we need to also handle cleanups in destroy().
+         */
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            cleanupAudioManager();
+        }
+    };
+
+    public CarVolumeDialogImpl(Context context) {
+        mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
+        mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+        mCar = Car.createCar(mContext, mServiceConnection);
+    }
+
+    private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+        try {
+            return carAudioManager.getGroupVolume(volumeGroupId);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        return 0;
+    }
+
+    private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
+        try {
+            return carAudioManager.getGroupMaxVolume(volumeGroupId);
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Build the volume window and connect to the CarService which registers with car audio
+     * manager.
+     */
+    @Override
+    public void init(int windowType, Callback callback) {
+        initDialog();
+
+        mCar.connect();
+    }
+
+    @Override
+    public void destroy() {
+        mHandler.removeCallbacksAndMessages(null);
+
+        cleanupAudioManager();
+        // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
+        // audio manager beforehand.
+        mCar.disconnect();
+    }
+
+    private void initDialog() {
+        loadAudioUsageItems();
+        mVolumeLineItems.clear();
+        mDialog = new CustomDialog(mContext);
+
+        mHovering = false;
+        mShowing = false;
+        mExpanded = false;
+        mWindow = mDialog.getWindow();
+        mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+        mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+        mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+        final WindowManager.LayoutParams lp = mWindow.getAttributes();
+        lp.format = PixelFormat.TRANSLUCENT;
+        lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+        lp.windowAnimations = -1;
+        mWindow.setAttributes(lp);
+        mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+        mDialog.setCanceledOnTouchOutside(true);
+        mDialog.setContentView(R.layout.car_volume_dialog);
+        mDialog.setOnShowListener(dialog -> {
+            mListView.setTranslationY(-mListView.getHeight());
+            mListView.setAlpha(0);
+            mListView.animate()
+                    .alpha(1)
+                    .translationY(0)
+                    .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+                    .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
+                    .start();
+        });
+        mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
+        mListView.setOnHoverListener((v, event) -> {
+            int action = event.getActionMasked();
+            mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
+                    || (action == MotionEvent.ACTION_HOVER_MOVE);
+            rescheduleTimeoutH();
+            return true;
+        });
+
+        mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
+                BackgroundStyle.PANEL);
+        mListView.setAdapter(mPagedListAdapter);
+        mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
+    }
+
+
+    private void showH(int reason) {
+        if (D.BUG) {
+            Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
+        }
+
+        mHandler.removeMessages(H.SHOW);
+        mHandler.removeMessages(H.DISMISS);
+        rescheduleTimeoutH();
+        // Refresh the data set before showing.
+        mPagedListAdapter.notifyDataSetChanged();
+        if (mShowing) {
+            return;
+        }
+        mShowing = true;
+
+        mDialog.show();
+        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+    }
+
+    private void rescheduleTimeoutH() {
+        mHandler.removeMessages(H.DISMISS);
+        final int timeout = computeTimeoutH();
+        mHandler.sendMessageDelayed(mHandler
+                .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
+
+        if (D.BUG) {
+            Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
+        }
+    }
+
+    private int computeTimeoutH() {
+        return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
+    }
+
+    private void dismissH(int reason) {
+        if (D.BUG) {
+            Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
+        }
+
+        mHandler.removeMessages(H.DISMISS);
+        mHandler.removeMessages(H.SHOW);
+        if (!mShowing) {
+            return;
+        }
+
+        mListView.animate().cancel();
+
+        mListView.setTranslationY(0);
+        mListView.setAlpha(1);
+        mListView.animate()
+                .alpha(0)
+                .translationY(-mListView.getHeight())
+                .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
+                .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+                .withEndAction(() -> mHandler.postDelayed(() -> {
+                    if (D.BUG) {
+                        Log.d(TAG, "mDialog.dismiss()");
+                    }
+                    mDialog.dismiss();
+                    mShowing = false;
+                }, DISMISS_DELAY_IN_MILLIS))
+                .start();
+
+        Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
+    }
+
+    private void loadAudioUsageItems() {
+        try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            int type;
+            // Traverse to the first start tag
+            while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+                    && type != XmlResourceParser.START_TAG) {
+                // Do Nothing (moving parser to start element)
+            }
+
+            if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
+                throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
+            }
+            int outerDepth = parser.getDepth();
+            int rank = 0;
+            while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT
+                    && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlResourceParser.END_TAG) {
+                    continue;
+                }
+                if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
+                    TypedArray item = mContext.getResources().obtainAttributes(
+                            attrs, R.styleable.carVolumeItems_item);
+                    int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
+                    if (usage >= 0) {
+                        VolumeItem volumeItem = new VolumeItem();
+                        volumeItem.rank = rank;
+                        volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon,
+                                0);
+                        mVolumeItems.put(usage, volumeItem);
+                        rank++;
+                    }
+                    item.recycle();
+                }
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Error parsing volume groups configuration", e);
+        }
+    }
+
+    private VolumeItem getVolumeItemForUsages(int[] usages) {
+        int rank = Integer.MAX_VALUE;
+        VolumeItem result = null;
+        for (int usage : usages) {
+            VolumeItem volumeItem = mVolumeItems.get(usage);
+            if (volumeItem.rank < rank) {
+                rank = volumeItem.rank;
+                result = volumeItem;
+            }
+        }
+        return result;
+    }
+
+    private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem,
+            int volumeGroupId,
+            int supplementalIconId,
+            @Nullable View.OnClickListener supplementalIconOnClickListener) {
+        SeekbarListItem listItem = new SeekbarListItem(mContext);
+        listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
+        int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
+        int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
+        listItem.setProgress(progress);
+        listItem.setOnSeekBarChangeListener(new CarVolumeDialogImpl
+                .VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
+        Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
+        primaryIcon.mutate().setTint(color);
+        listItem.setPrimaryActionIcon(primaryIcon);
+        if (supplementalIconId != 0) {
+            Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
+            supplementalIcon.mutate().setTint(color);
+            listItem.setSupplementalIcon(supplementalIcon, true);
+            listItem.setSupplementalIconListener(supplementalIconOnClickListener);
+        } else {
+            listItem.setSupplementalEmptyIcon(true);
+            listItem.setSupplementalIconListener(null);
+        }
+
+        mVolumeLineItems.add(listItem);
+        volumeItem.listItem = listItem;
+        volumeItem.progress = progress;
+        return listItem;
+    }
+
+    private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
+        for (int i = 0; i < mVolumeItems.size(); ++i) {
+            VolumeItem volumeItem = mVolumeItems.valueAt(i);
+            if (volumeItem.listItem == targetItem) {
+                return volumeItem;
+            }
+        }
+        return null;
+    }
+
+    private void cleanupAudioManager() {
+        try {
+            mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
+        } catch (CarNotConnectedException e) {
+            Log.e(TAG, "Car is not connected!", e);
+        }
+        mVolumeLineItems.clear();
+        mCarAudioManager = null;
+    }
+
+    /**
+     * Wrapper class which contains information of each volume group.
+     */
+    private static class VolumeItem {
+
+        private int rank;
+        private boolean defaultItem = false;
+        private @DrawableRes int icon;
+        private SeekbarListItem listItem;
+        private int progress;
+    }
+
+    private final class H extends Handler {
+
+        private static final int SHOW = 1;
+        private static final int DISMISS = 2;
+
+        private H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SHOW:
+                    showH(msg.arg1);
+                    break;
+                case DISMISS:
+                    dismissH(msg.arg1);
+                    break;
+                default:
+            }
+        }
+    }
+
+    private final class CustomDialog extends Dialog implements DialogInterface {
+
+        private CustomDialog(Context context) {
+            super(context, com.android.systemui.R.style.qs_theme);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            rescheduleTimeoutH();
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected void onStart() {
+            super.setCanceledOnTouchOutside(true);
+            super.onStart();
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (isShowing()) {
+                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                    mHandler.obtainMessage(
+                            H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private final class ExpandIconListener implements View.OnClickListener {
+
+        @Override
+        public void onClick(final View v) {
+            mExpanded = !mExpanded;
+            Animator inAnimator;
+            if (mExpanded) {
+                for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
+                    // Adding the items which are not coming from the default item.
+                    VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
+                    if (volumeItem.defaultItem) {
+                        // Set progress here due to the progress of seekbar may not be updated.
+                        volumeItem.listItem.setProgress(volumeItem.progress);
+                    } else {
+                        addSeekbarListItem(volumeItem, groupId, 0, null);
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                        mContext, R.anim.car_arrow_fade_in_rotate_up);
+            } else {
+                // Only keeping the default stream if it is not expended.
+                Iterator itr = mVolumeLineItems.iterator();
+                while (itr.hasNext()) {
+                    SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
+                    VolumeItem volumeItem = findVolumeItem(seekbarListItem);
+                    if (!volumeItem.defaultItem) {
+                        itr.remove();
+                    } else {
+                        // Set progress here due to the progress of seekbar may not be updated.
+                        seekbarListItem.setProgress(volumeItem.progress);
+                    }
+                }
+                inAnimator = AnimatorInflater.loadAnimator(
+                        mContext, R.anim.car_arrow_fade_in_rotate_down);
+            }
+
+            Animator outAnimator = AnimatorInflater.loadAnimator(
+                    mContext, R.anim.car_arrow_fade_out);
+            inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
+            AnimatorSet animators = new AnimatorSet();
+            animators.playTogether(outAnimator, inAnimator);
+            animators.setTarget(v);
+            animators.start();
+            mPagedListAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+
+        private final int mVolumeGroupId;
+        private final CarAudioManager mCarAudioManager;
+
+        private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
+            mVolumeGroupId = volumeGroupId;
+            mCarAudioManager = carAudioManager;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (!fromUser) {
+                // For instance, if this event is originated from AudioService,
+                // we can ignore it as it has already been handled and doesn't need to be
+                // sent back down again.
+                return;
+            }
+            try {
+                if (mCarAudioManager == null) {
+                    Log.w(TAG, "Ignoring volume change event because the car isn't connected");
+                    return;
+                }
+                mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
+                mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car is not connected!", e);
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+        }
+    }
+}
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 7057ce6..aef6a3c 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -9,4 +9,5 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
\ No newline at end of file
+refuhoo@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index 22d0e3b..0cad5af 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -19,8 +19,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -241,7 +240,7 @@
             signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         }
         if (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) == 1) {
+                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
             if (mNotificationCategorizer.shouldSilence(entry)) {
                 final int importance = entry.getImportance() < IMPORTANCE_LOW
                         ? entry.getImportance() : IMPORTANCE_LOW;
@@ -378,6 +377,14 @@
     }
 
     @Override
+    public void onActionClicked(String key, Notification.Action action, int source) {
+        if (DEBUG) {
+            Log.d(TAG, "onActionClicked() called with: key = [" + key + "], action = [" + action.title
+                    + "], source = [" + source + "]");
+        }
+    }
+
+    @Override
     public void onListenerConnected() {
         if (DEBUG) Log.i(TAG, "CONNECTED");
         try {
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index b2fc417..892267b 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -48,7 +48,7 @@
                     | Notification.FLAG_NO_CLEAR;
     private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
     private static final int MAX_ACTIONS_PER_LINK = 1;
-    private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+    private static final int MAX_SMART_ACTIONS = 3;
     private static final int MAX_SUGGESTED_REPLIES = 3;
 
     private static final ConversationActions.TypeConfig TYPE_CONFIG =
@@ -81,12 +81,9 @@
         if (tcm == null) {
             return EMPTY_ACTION_LIST;
         }
-        Notification.Action[] actions = entry.getNotification().actions;
-        int numOfExistingActions = actions == null ? 0: actions.length;
-        int maxSmartActions = MAX_SMART_ACTIONS - numOfExistingActions;
         return suggestActionsFromText(
                 tcm,
-                getMostSalientActionText(entry.getNotification()), maxSmartActions);
+                getMostSalientActionText(entry.getNotification()), MAX_SMART_ACTIONS);
     }
 
     ArrayList<CharSequence> suggestReplies(
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
deleted file mode 100644
index bc06cab..0000000
--- a/packages/PackageInstaller/Android.bp
+++ /dev/null
@@ -1,14 +0,0 @@
-android_app {
-    name: "PackageInstaller",
-
-    srcs: ["src/**/*.java"],
-
-    static_libs: [
-        "androidx.leanback_leanback",
-        "xz-java",
-    ],
-
-    certificate: "platform",
-    privileged: true,
-    platform_apis: true,
-}
\ No newline at end of file
diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk
new file mode 100644
index 0000000..ab5483c
--- /dev/null
+++ b/packages/PackageInstaller/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2018 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PackageInstaller
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := xz-java
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.leanback_leanback
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 4801f62..eb9ec82 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -2,6 +2,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.packageinstaller">
 
+    <original-package android:name="com.android.packageinstaller" />
+
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 91e23dd..03eafc4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -34,15 +34,23 @@
     <uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
     <uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
 
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+                     android:dataSentOffDevice="no"
+                     android:dataSharedWithThirdParty="no"
+                     android:dataUsedForMonetization="no"
+                     android:dataRetentionTime="unlimited"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
+                     android:dataSentOffDevice="no"
+                     android:dataSharedWithThirdParty="no"
+                     android:dataUsedForMonetization="no"
+                     android:dataRetentionTime="unlimited"/>
+
     <application
         android:allowClearUserData="true"
         android:label="@string/app_label"
-        android:allowBackup= "false"
         android:supportsRtl="true">
 
         <service
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
index 0ccf13e..9e16f5e 100644
--- a/packages/PrintSpooler/res/layout/print_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -107,7 +107,7 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="16dip"
         android:elevation="@dimen/preview_controls_elevation"
-        android:tint="?android:attr/textColorPrimaryInverse"
+        android:tint="@android:color/white"
         android:background="@drawable/print_button">
     </ImageButton>
 
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
new file mode 100644
index 0000000..e518e0b
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "ActionButtonsPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+          "androidx.preference_preference",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
new file mode 100644
index 0000000..4b9f1ab
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
new file mode 100644
index 0000000..4f47113
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout/settings_action_buttons.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="8dp"
+    android:orientation="horizontal">
+
+    <Button
+        android:id="@+id/button1"
+        style="@style/SettingsActionButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/button2"
+        style="@style/SettingsActionButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/button3"
+        style="@style/SettingsActionButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/button4"
+        style="@style/SettingsActionButton"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
new file mode 100644
index 0000000..efa508d
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
+        <item name="android:drawablePadding">4dp</item>
+        <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
+        <item name="android:layout_marginEnd">8dp</item>
+        <item name="android:paddingTop">20dp</item>
+        <item name="android:paddingBottom">20dp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
new file mode 100644
index 0000000..8b46cc6
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2018 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.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.StringRes;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+/**
+ * This preference provides a four buttons layout with Settings style.
+ * It looks like below
+ *
+ * --------------------------------------------------
+ * button1     | button2   | button3   | button4   |
+ * --------------------------------------------------
+ *
+ * User can set title / icon / click listener for each button.
+ *
+ * By default, four buttons are visible.
+ * However, there are two cases which button should be invisible(View.GONE).
+ *
+ * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false)
+ * 2. User doesn't set any title or icon for button.
+ */
+public class ActionButtonsPreference extends Preference {
+
+    private static final String TAG = "ActionButtonPreference";
+    private final ButtonInfo mButton1Info = new ButtonInfo();
+    private final ButtonInfo mButton2Info = new ButtonInfo();
+    private final ButtonInfo mButton3Info = new ButtonInfo();
+    private final ButtonInfo mButton4Info = new ButtonInfo();
+
+    public ActionButtonsPreference(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    public ActionButtonsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    public ActionButtonsPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public ActionButtonsPreference(Context context) {
+        super(context);
+        init();
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.settings_action_buttons);
+        setSelectable(false);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        holder.setDividerAllowedAbove(true);
+        holder.setDividerAllowedBelow(true);
+
+        mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
+        mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
+        mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
+        mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
+
+        mButton1Info.setUpButton();
+        mButton2Info.setUpButton();
+        mButton3Info.setUpButton();
+        mButton4Info.setUpButton();
+    }
+
+    /**
+     * Set the visibility state of button1.
+     */
+    public ActionButtonsPreference setButton1Visible(boolean isVisible) {
+        if (isVisible != mButton1Info.mIsVisible) {
+            mButton1Info.mIsVisible = isVisible;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the text to be displayed in button1.
+     */
+    public ActionButtonsPreference setButton1Text(@StringRes int textResId) {
+        final String newText = getContext().getString(textResId);
+        if (!TextUtils.equals(newText, mButton1Info.mText)) {
+            mButton1Info.mText = newText;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the drawable to be displayed above of text in button1.
+     */
+    public ActionButtonsPreference setButton1Icon(@DrawableRes int iconResId) {
+        if (iconResId == 0) {
+            return this;
+        }
+
+        final Drawable icon;
+        try {
+            icon = getContext().getDrawable(iconResId);
+            mButton1Info.mIcon = icon;
+            notifyChanged();
+        } catch (Resources.NotFoundException exception) {
+            Log.e(TAG, "Resource does not exist: " + iconResId);
+        }
+        return this;
+    }
+
+    /**
+     * Set the enabled state of button1.
+     */
+    public ActionButtonsPreference setButton1Enabled(boolean isEnabled) {
+        if (isEnabled != mButton1Info.mIsEnabled) {
+            mButton1Info.mIsEnabled = isEnabled;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Register a callback to be invoked when button1 is clicked.
+     */
+    public ActionButtonsPreference setButton1OnClickListener(
+            View.OnClickListener listener) {
+        if (listener != mButton1Info.mListener) {
+            mButton1Info.mListener = listener;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Set the visibility state of button2.
+     */
+    public ActionButtonsPreference setButton2Visible(boolean isVisible) {
+        if (isVisible != mButton2Info.mIsVisible) {
+            mButton2Info.mIsVisible = isVisible;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the text to be displayed in button2.
+     */
+    public ActionButtonsPreference setButton2Text(@StringRes int textResId) {
+        final String newText = getContext().getString(textResId);
+        if (!TextUtils.equals(newText, mButton2Info.mText)) {
+            mButton2Info.mText = newText;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the drawable to be displayed above of text in button2.
+     */
+    public ActionButtonsPreference setButton2Icon(@DrawableRes int iconResId) {
+        if (iconResId == 0) {
+            return this;
+        }
+
+        final Drawable icon;
+        try {
+            icon = getContext().getDrawable(iconResId);
+            mButton2Info.mIcon = icon;
+            notifyChanged();
+        } catch (Resources.NotFoundException exception) {
+            Log.e(TAG, "Resource does not exist: " + iconResId);
+        }
+        return this;
+    }
+
+    /**
+     * Set the enabled state of button2.
+     */
+    public ActionButtonsPreference setButton2Enabled(boolean isEnabled) {
+        if (isEnabled != mButton2Info.mIsEnabled) {
+            mButton2Info.mIsEnabled = isEnabled;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Register a callback to be invoked when button2 is clicked.
+     */
+    public ActionButtonsPreference setButton2OnClickListener(
+            View.OnClickListener listener) {
+        if (listener != mButton2Info.mListener) {
+            mButton2Info.mListener = listener;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Set the visibility state of button3.
+     */
+    public ActionButtonsPreference setButton3Visible(boolean isVisible) {
+        if (isVisible != mButton3Info.mIsVisible) {
+            mButton3Info.mIsVisible = isVisible;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the text to be displayed in button3.
+     */
+    public ActionButtonsPreference setButton3Text(@StringRes int textResId) {
+        final String newText = getContext().getString(textResId);
+        if (!TextUtils.equals(newText, mButton3Info.mText)) {
+            mButton3Info.mText = newText;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the drawable to be displayed above of text in button3.
+     */
+    public ActionButtonsPreference setButton3Icon(@DrawableRes int iconResId) {
+        if (iconResId == 0) {
+            return this;
+        }
+
+        final Drawable icon;
+        try {
+            icon = getContext().getDrawable(iconResId);
+            mButton3Info.mIcon = icon;
+            notifyChanged();
+        } catch (Resources.NotFoundException exception) {
+            Log.e(TAG, "Resource does not exist: " + iconResId);
+        }
+        return this;
+    }
+
+    /**
+     * Set the enabled state of button3.
+     */
+    public ActionButtonsPreference setButton3Enabled(boolean isEnabled) {
+        if (isEnabled != mButton3Info.mIsEnabled) {
+            mButton3Info.mIsEnabled = isEnabled;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Register a callback to be invoked when button3 is clicked.
+     */
+    public ActionButtonsPreference setButton3OnClickListener(
+            View.OnClickListener listener) {
+        if (listener != mButton3Info.mListener) {
+            mButton3Info.mListener = listener;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Set the visibility state of button4.
+     */
+    public ActionButtonsPreference setButton4Visible(boolean isVisible) {
+        if (isVisible != mButton4Info.mIsVisible) {
+            mButton4Info.mIsVisible = isVisible;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the text to be displayed in button4.
+     */
+    public ActionButtonsPreference setButton4Text(@StringRes int textResId) {
+        final String newText = getContext().getString(textResId);
+        if (!TextUtils.equals(newText, mButton4Info.mText)) {
+            mButton4Info.mText = newText;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Sets the drawable to be displayed above of text in button4.
+     */
+    public ActionButtonsPreference setButton4Icon(@DrawableRes int iconResId) {
+        if (iconResId == 0) {
+            return this;
+        }
+
+        final Drawable icon;
+        try {
+            icon = getContext().getDrawable(iconResId);
+            mButton4Info.mIcon = icon;
+            notifyChanged();
+        } catch (Resources.NotFoundException exception) {
+            Log.e(TAG, "Resource does not exist: " + iconResId);
+        }
+        return this;
+    }
+
+    /**
+     * Set the enabled state of button4.
+     */
+    public ActionButtonsPreference setButton4Enabled(boolean isEnabled) {
+        if (isEnabled != mButton4Info.mIsEnabled) {
+            mButton4Info.mIsEnabled = isEnabled;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    /**
+     * Register a callback to be invoked when button4 is clicked.
+     */
+    public ActionButtonsPreference setButton4OnClickListener(
+            View.OnClickListener listener) {
+        if (listener != mButton4Info.mListener) {
+            mButton4Info.mListener = listener;
+            notifyChanged();
+        }
+        return this;
+    }
+
+    static class ButtonInfo {
+        private Button mButton;
+        private CharSequence mText;
+        private Drawable mIcon;
+        private View.OnClickListener mListener;
+        private boolean mIsEnabled = true;
+        private boolean mIsVisible = true;
+
+        void setUpButton() {
+            mButton.setText(mText);
+            mButton.setOnClickListener(mListener);
+            mButton.setEnabled(mIsEnabled);
+            mButton.setCompoundDrawablesWithIntrinsicBounds(
+                    null /* left */, mIcon /* top */, null /* right */, null /* bottom */);
+
+            if (shouldBeVisible()) {
+                mButton.setVisibility(View.VISIBLE);
+            } else {
+                mButton.setVisibility(View.GONE);
+            }
+        }
+
+        /**
+         * By default, four buttons are visible.
+         * However, there are two cases which button should be invisible.
+         *
+         * 1. User set invisible for this button. ex: mIsVisible = false.
+         * 2. User didn't set any title or icon.
+         */
+        private boolean shouldBeVisible() {
+            return mIsVisible && (!TextUtils.isEmpty(mText) || mIcon != null);
+        }
+    }
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 444e724..cc17b25 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -17,6 +17,7 @@
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
         "SettingsLayoutPreference",
+        "ActionButtonsPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/SearchWidget/res/values-as/strings.xml b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
new file mode 100644
index 0000000..813e764
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="search_menu" msgid="1604061903696928905">"সন্ধান সম্পৰ্কীয় ছেটিংসমূহ"</string>
+</resources>
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
index 0678263..01d9c00 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -35,7 +35,7 @@
             android:id="@+id/entity_header_icon"
             android:layout_width="48dp"
             android:layout_height="48dp"
-            android:scaleType="fitXY"
+            android:scaleType="fitCenter"
             android:antialias="true"/>
 
         <TextView
@@ -72,7 +72,6 @@
     </LinearLayout>
 
     <LinearLayout
-        android:id="@+id/entity_header_links"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_centerVertical="true"
@@ -85,6 +84,7 @@
             android:layout_width="wrap_content"
             android:layout_weight="1"
             android:layout_height="0dp"
+            android:visibility="gone"
             android:minWidth="24dp"
             android:src="@null"
             android:tint="?android:attr/colorAccent"/>
@@ -95,6 +95,7 @@
             android:layout_width="wrap_content"
             android:layout_weight="1"
             android:layout_height="0dp"
+            android:visibility="gone"
             android:minWidth="24dp"
             android:src="@null"
             android:tint="?android:attr/colorAccent"/>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
new file mode 100644
index 0000000..1a47afc
--- /dev/null
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="@android:color/transparent"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="?android:attr/selectableItemBackground"
+        android:gravity="start|center_vertical"
+        android:clipToPadding="false"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+        <LinearLayout
+            android:id="@+id/checkbox_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:minWidth="56dp"
+            android:orientation="horizontal"
+            android:clipToPadding="false"
+            android:paddingTop="4dp"
+            android:paddingBottom="4dp">
+            <include layout="@layout/preference_widget_checkbox" />
+        </LinearLayout>
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:paddingTop="16dp"
+            android:paddingBottom="16dp">
+
+            <TextView
+                android:id="@android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceListItem"
+                android:ellipsize="marquee" />
+
+            <TextView
+                android:id="@android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@android:id/title"
+                android:layout_alignStart="@android:id/title"
+                android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="10" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <include layout="@layout/preference_two_target_divider" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="64dp"
+        android:gravity="center"
+        android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index d34820c..1dd7838 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sal op grond van jou gebruik waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vra elke keer"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat jy dit afskakel"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sopas"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aansluitingprogram om opgedateerde grafikadrywer in ontwikkeling te gebruik"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Foonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b595e2b..2ce5f44 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰናክሏል"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"በአጠቃቀምዎ መሠረት እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"እስከ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ገደማ ድረስ መቆየት አለበት"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"እስከ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ከ<xliff:g id="TIME_REMAINING">%1$s</xliff:g> በላይ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ሁልጊዜ ጠይቅ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"እስኪያጠፉት ድረስ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ልክ አሁን"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"በግንባታ ላይ የተዘመነ የግራፊክስ ነጂን ለመጠቀም መተግበሪያን መርጠው ያስገቡ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"የስልክ ድምጽ ማጉያ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index f8c2ba2..23f3a12 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"بدون"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غير مفعّلة"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏تعذّرت تهيئة عنوان IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -451,4 +453,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"الطلب في كل مرة"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"إلى أن توقف الوضع يدويًا"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"للتو"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"فعِّل التطبيق لاستخدام برنامج تشغيل الرسومات المُحدَّث في تطوير البرامج."</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"مكبر صوت الهاتف"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 72122c2..86d1459 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"নাই"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"নিষ্ক্ৰিয় হৈ আছে"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"বেটাৰি আনুমানিকভাৱে <xliff:g id="TIME">%1$s</xliff:g> লৈকে চলিব"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পৰ্যন্ত"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>তকৈও কম সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>তকৈও বেছি সময় বাকী আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্ৰতিবাৰতে সোধক"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"আপুনি অফ নকৰা পর্যন্ত"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এই মাত্ৰ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"বিকাশকাৰ্য চলি থকা আপডে\'টেড গ্ৰাফিক ড্ৰাইভাৰ ব্যৱহাৰ কৰিবলৈ এপ্ অপ্ট ইন কৰক"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফ’নৰ স্পীকাৰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 23008e2..17d0ca6 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Qalan vaxt <xliff:g id="TIME_REMAINING">%1$s</xliff:g> və daha çoxdur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Hər dəfə soruşun"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deaktiv edənə qədər"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"İndicə"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Qrafik drayverdən istifadə etmək üçün tətbiq seçin"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon spikeri"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3ee8589..56052d2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nije moguće skenirati mreže"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> na osnovu korišćenja"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogući aplikaciju za korišćenje upravljačkog programa grafičke katice u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 64253bf..f2c2046 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Захавана"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Адключана"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Збой канфігурацыі IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g> пры цяперашнім узроўні выкарыстання"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Зараду (<xliff:g id="LEVEL">%2$s</xliff:g>) хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Да <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць менш чым на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Узровень зараду батарэі: <xliff:g id="LEVEL">%2$s</xliff:g> (хопіць больш чым на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Заўсёды пытацца"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пакуль не выключыце"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Зараз"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Выбраная праграма, якая выкарыстоўвае абноўлены драйвер графічнай сістэмы (падчас распрацоўкі)"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Дынамік тэлефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 668aa2d..5b8d6b8 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Да се пита винаги"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"До изключване"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Току-що"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Включване на приложението за използване на актуализирания графичен драйвер в разработка"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Високоговорител на телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index c6eed2c..7efad99 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"বর্তমান ব্যবহার অনুযায়ী আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"আনুমানিক <xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত চলবে"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> পর্যন্ত"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> এর থেকেও কম বাকি আছে"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"আর <xliff:g id="THRESHOLD">%1$s</xliff:g>-এর কম চার্জ বাকি আছে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"আরও <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-এর বেশি চলবে (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"প্রতিবার জিজ্ঞেস করা হবে"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"এখনই"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ডেভলপমেন্টে আপডেট হওয়া গ্রাফিক্স ড্রাইভার ব্যবহার করতে অ্যাপ বেছে নিন"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ফেনের স্পিকার"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 16179fb..92435f7 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g> na osnovu vaše upotrebe"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Trebala bi trajati do otprilike <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prijavi aplikaciju za korištenje ažuriranog grafičkog drajvera u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f906ea4..3182f16 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> segons l\'ús que en fas"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"La bateria hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pregunta sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fins que no ho desactivis"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ara mateix"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicació activada per utilitzar el controlador de gràfics actualitzat en desenvolupament"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altaveu del telèfon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index f9c8669..3785433 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Při vašem obvyklém využití vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pokaždé se zeptat"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokud tuto funkci nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Právě teď"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Přihlaste aplikaci k použití vyvíjeného aktualizovaného grafického ovladače"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6b2bd98..eeeccee 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> baseret på dit forbrug"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spørg hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Indtil du deaktiverer"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Lige nu"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Tilvælg en app, der skal bruge den opdaterede grafikdriver under udvikling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonens højttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ad6f5f2..95f7416 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sollte basierend auf deiner Nutzung etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sollte etwa bis <xliff:g id="TIME">%1$s</xliff:g> reichen"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Bis <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Jedes Mal fragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bis zur Deaktivierung"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Gerade eben"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App aktivieren, um den aktualisierten Grafiktreiber in der Entwicklung zu verwenden"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon-Lautsprecher"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 5bfbf2c..310b6cf 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου, ανάλογα με τη χρήση σας"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Θα διαρκέσει μέχρι τις <xliff:g id="TIME">%1$s</xliff:g> περίπου"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Έως τις <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Απομένουν περισσότερα/ες από <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Να ερωτώμαι κάθε φορά"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Μέχρι την απενεργοποίηση"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Μόλις τώρα"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Επιλέξτε μια εφαρμογή για τη χρήση του ενημερωμένου προγράμματος οδήγησης γραφικών σε ανάπτυξη"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Ηχείο τηλεφώνου"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f0eaeed..5b11d63 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Until <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"More than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ask every time"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Until you turn off"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Just now"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Opt in app to use updated graphics driver in development"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Phone speaker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 6927fda..d7ed49a 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎Can\'t scan for networks‎‏‎‎‏‎"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‎None‎‏‎‎‏‎"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎Saved‎‏‎‎‏‎"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‎Disabled‎‏‎‎‏‎"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‎IP Configuration Failure‎‏‎‎‏‎"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ based on your usage‎‏‎‎‏‎"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‎‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‏‎Should last until about ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎Less than ‎‏‎‎‏‏‎<xliff:g id="THRESHOLD">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining‎‏‎‎‏‎"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎Less than ‎‏‎‎‏‏‎<xliff:g id="THRESHOLD">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎More than ‎‏‎‎‏‏‎<xliff:g id="TIME_REMAINING">%1$s</xliff:g>‎‏‎‎‏‏‏‎ remaining (‎‏‎‎‏‏‎<xliff:g id="LEVEL">%2$s</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎Ask every time‎‏‎‎‏‎"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎Until you turn off‎‏‎‎‏‎"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎Just now‎‏‎‎‏‎"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎Opt in app to use updated graphcis driver in developement‎‏‎‎‏‎"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎Phone speaker‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 675084f..ecec63c 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que lo desactives"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Recién"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar app para que use el controlador de gráficos actualizado en el desarrollo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index b51b847..b8fdf30 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> según el uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Queda menos del <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Queda más del <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar siempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hasta que se desactive"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Justo ahora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Habilitar aplicación para usar controlador de gráficos actualizado en desarrollo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altavoz del teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fb077be..1246626 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Peaks teie kasutuse põhjal kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Jäänud on üle <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Küsi iga kord"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kuni välja lülitate"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsja"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lubage rakendus, et kasutada arenduses olevat värskendatud graafikadraiverit"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoni kõlar"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3d3b8c3..68b4840 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Erabileraren arabera, ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> arte"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen dira"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago gelditzen da (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Galdetu beti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Desaktibatu arte"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Oraintxe"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Hautatu zein aplikaziorekin erabili nahi duzun garatze-prozesuan dagoen grafikoen kontrolatzaile eguneratua"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonoaren bozgorailua"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0af118e..7882b056 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکه‌ها امکان‌پذیر نیست"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"هیچ‌کدام"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ذخیره‌شده"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏پیکربندی IP انجام نشد"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"باتوجه به میزان مصرفتان، باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) شارژ داشته باشید"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"تا <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> باقی مانده"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"هربار پرسیده شود"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"تا زمانی‌که آن را خاموش کنید"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"هم‌اکنون"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"برنامه انتخاب‌شده برای استفاده از درایور گرافیک به‌روزرسانی‌شده در برنامه‌نویس"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"بلندگوی تلفن"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index dde10d0d..1904c4d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Varaus loppuu käyttösi perusteella noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> saakka"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Kysy aina"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kunnes poistat sen käytöstä"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Äsken"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Lisää sovellus käyttämään päivitettyä grafiikkaohjainta kehitysvaiheessa"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Puhelimen kaiutin"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1257ea5..45b2872 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>, en fonction de votre usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionnez l\'application pour utiliser le pilote graphique mis à jour en mode de conception"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 444a52e..13361d7 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Toujours demander"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Jusqu\'à la désactivation"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"À l\'instant"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sélectionner une application pour le développement de laquelle utiliser le pilote graphique mis à jour"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Haut-parleur du téléphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8df2697..e8cf009 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"En función do uso, debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Ata: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo restante inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Preguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Ata a desactivación"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Subscribirse á aplicación para utilizar o controlador de gráficos actualizado en desenvolvemento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altofalante do teléfono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4a92f2a..918920d 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"તમારા વપરાશના આધારે લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> સુધી"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ સમય બાકી છે (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"દર વખતે પૂછો"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"તમે બંધ ન કરો ત્યાં સુધી"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"હમણાં જ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"અપડેટ કરેલ ગ્રાફિક્સ ડ્રાઇવરનો ઉપયોગ કરવા માટે અ‍ૅપ પસંદ કરો"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ફોન સ્પીકર"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6d88c35..890d036 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्‍कैन नहीं कर सकता"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सेव किया गया"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्‍फ़िगरेशन की विफलता"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"आपके इस्तेमाल के हिसाब से बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"बैटरी लगभग <xliff:g id="TIME">%1$s</xliff:g> चलेगी"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम समय बचा है"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"डेवलपमेंट में अपडेट किए गए ग्राफ़िक्स ड्राइवर का इस्तेमाल करने के लिए ऐप्लिकेशन में ऑप्ट इन करें"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फ़ोन स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 32c9a62..1464b91 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> na temelju vaše upotrebe"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Otprilike bi trebalo trajati do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pitaj svaki put"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sad"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uključi aplikaciju za upotrebu ažuriranog upravljačkog programa u razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvučnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ed19267..c9fdd45 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"A használat alapján nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Nagyjából még ennyit bír: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Kevesebb mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Mindig kérdezzen rá"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kikapcsolásig"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az imént"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"A frissített, fejlesztés alatt álló grafikus drivert használja a választott alkalmazás"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hangszórója"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 6d516ea..3b13ed1 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>՝ կախված օգտագործման եղանակից"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Լիցքը (<xliff:g id="LEVEL">%2$s</xliff:g>) պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Լիցքը պետք է, որ բավականացնի մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Հարցնել ամեն անգամ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Մինչև չանջատեք"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Հենց նոր"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ընտրված հավելվածը, որը պետք է օգտագործի թարմացված գրաֆիկական սարքավարը մշակման ժամանակ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Հեռախոսի բարձրախոս"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a5f2317..4d1b2df 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Tidak ada"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan Anda"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Akan bertahan kira-kira sampai <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Selalu tanya"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sampai Anda menonaktifkannya"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Baru saja"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ikut sertakan aplikasi untuk menggunakan driver grafis yang diupdate dalam pengembangan"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ponsel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 6cf09a6..600ffa4 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> miðað við notkun þína"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til klukkan <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spyrja í hvert skipti"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Þar til þú slekkur"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Rétt í þessu"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velja að nota uppfærðan myndefnisrekil í þróun í forriti"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Símahátalari"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index a1b9b82..068aa91 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tempo stimato rimanente in base al tuo utilizzo: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Tempo stimato rimanente: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Fino alle ore <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Chiedi ogni volta"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Fino alla disattivazione"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Adesso"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Attiva l\'app per utilizzare il driver grafico aggiornato nella versione di sviluppo"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlante telefono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 1a50622..fa38f77 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏כשל בתצורת IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> על סמך השימוש במכשיר"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"נותרו יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"שאל בכל פעם"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"עד הכיבוי"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"הרגע"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"האפליקציה שנבחרה לשימוש במנהל ההתקן המעודכן לגרפיקה שבפיתוח"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"רמקול של טלפון"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4c544af..9927654 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"電池切れの推定時間: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"毎回確認"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"OFF にするまで"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"たった今"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"更新したグラフィックス ドライバを開発に使用するオプトイン アプリ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"スマートフォンのスピーカー"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 92f3049..7ba0153 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>, მოხმარების გათვალისწინებით"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"დარჩენილია <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი დრო (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ყოველთვის მკითხეთ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"გამორთვამდე"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ახლახან"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"გააქტიურების აპი, რომელიც გამოიყენებს შემუშავების პროცესში მყოფ, განახლებულ გრაფიკულ დრაივერს"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ტელეფონის დინამიკი"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b56c6fd..d5b4441 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Пайдалануға байланысты шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) уақытқа жетеді"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> уақытқа жетеді"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> дейін"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> шамасынан көп уақыт қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Әрдайым сұрау"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Өшірілгенге дейін"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дәл қазір"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Әзірлеу барысында қолданба жаңартылған графика драйверін пайдаланады"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефон динамигі"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5860473..d1d1c76 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិន​អាច​វិភាគ​រក​បណ្ដាញ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"បាន​រក្សាទុក"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"បាន​បិទ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការ​កំណត់​រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> ដោយផ្អែក​លើការ​ប្រើប្រាស់​របស់អ្នក"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"គួរ​តែ​អាច​ប្រើបាន​រហូតដល់​ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"រហូតដល់​ម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"នៅ​សល់​តិច​ជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"នៅសល់​តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"នៅសល់​ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"សួរគ្រប់ពេល"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"រហូតទាល់តែ​អ្នកបិទ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"អម្បាញ់មិញ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ភ្ជាប់កម្មវិធី​ ដើម្បី​ប្រើដ្រាយវើ​ក្រាហ្វិក​ដែលបាន​ដំឡើងជំនាន់​សម្រាប់​ការ​អភិវឌ្ឍ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ឧបករណ៍​បំពង​សំឡេង​ទូរសព្ទ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 87e18e6..46759f5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್‌ವರ್ಕ್‌ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್‌ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ನಿಮ್ಮ ಬಳಕೆ ಆಧರಿಸಿ <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ಸಮಯದವರೆಗೆ ಫೋನ್‌ ರನ್‌ ಆಗಬೇಕು"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ರವರೆಗೆ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು (<xliff:g id="LEVEL">%2$s</xliff:g>) ಬಾಕಿ"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ಪ್ರತಿ ಬಾರಿ ಕೇಳಿ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ಇದೀಗ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ಅಭಿವೃದ್ಧಿಯಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡಲಾದ ಗ್ರಾಫಿಕ್‌ಗಳ ಡ್ರೈವರ್ ಬಳಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ಫೋನ್ ಸ್ಪೀಕರ್"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 3333c0d..067175b 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"약 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -435,7 +437,7 @@
     <string name="cancel" msgid="6859253417269739139">"취소"</string>
     <string name="okay" msgid="1997666393121016642">"확인"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"켜기"</string>
-    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"알림 일시중지 사용 설정"</string>
+    <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"방해 금지 모드 사용 설정"</string>
     <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"사용 안함"</string>
     <string name="zen_interruption_level_priority" msgid="2078370238113347720">"중요 알림만"</string>
     <string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"항상 확인"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"사용 중지할 때까지"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"조금 전"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"개발 중인 업데이트된 그래픽 드라이버를 사용할 앱을 선택하세요."</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"휴대전화 스피커"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cbbc47c..8e994da 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Колдонгонуңузга караганда болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) кийин өчөт"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> кийин өчөт"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> чейин"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ашыгыраак убакыт калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Ар дайым суралсын"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Бул функция өчүрүлгөнгө чейин"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Азыр эле"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Иштеп чыгууда жаңыртылган графикалык драйверлерди пайдалануу үчүн колдонмону кошуңуз"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефондун динамиги"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 21e4679..7bf46c0 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ບັນ​ທຶກແລ້ວ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການ​ຕັ້ງ​ຄ່າ IP ລົ້ມ​ເຫຼວ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> based on your usage"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Should last until about <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Should last until about <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"ຈົນກວ່າຈະຮອດ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ຖາມທຸກເທື່ອ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ຈົນກວ່າທ່ານຈະປິດ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ຕອນນີ້"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ເຂົ້າຮ່ວມແອັບເພື່ອໃຊ້ໄດຣເວີກຣາຟິກທີ່ອັບເດດແລ້ວໃນການພັດທະນາ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ລຳໂພງໂທລະສັບ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index cbff9e7..9da3d52 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Pagal tai, kaip naudojama, turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Iki <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Klausti kaskart"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Kol išjungsite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ką tik"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Pasirinkti programą, kuri bus naudojama su atnaujinta kuriama grafikos tvarkykle"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefono garsiakalbis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d200828..2233468 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Ņemot vērā lietojumu, darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Darbosies aptuveni līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Līdz <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Atlikušais laiks — mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Atlicis mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Atlicis vairāk nekā <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vaicāt katru reizi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Līdz brīdim, kad izslēgsiet"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Tikko"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Izvēlēties izmantot atjaunināto grafikas dzini šīs lietotnes izstrādē"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Tālruņa skaļrunis"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index db16847..534d154 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> според вашето користење"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Треба да трае до околу <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уште повеќе од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Секогаш прашувај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Додека не го исклучите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Неодамнешни"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Прифатете ја апликацијата за да се користи ажурираниот драјвер за графика во програмирање"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Телефонски звучник"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index c3af968..7a335cd 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെ‌റ്റ്‌വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ് (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> വരെ നീണ്ടുനിൽക്കേണ്ടതാണ്"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> വരെ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"എപ്പോഴും ചോദിക്കുക"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ഇപ്പോൾ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"വികസനത്തിൽ, അപ്ഡേറ്റ് ചെയ്‌ത ഗ്രാഫിക്‌സ് ഡ്രൈവർ ഉപയോഗിക്കാൻ ഓപ്റ്റ് ഇൻ ചെയ്യുക"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ഫോൺ സ്‌പീക്കർ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 8627e1b..4816643 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Таны хэрэглээнд тулгуурлан ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> хүртэл"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их хугацаа үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Тухай бүрт асуух"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Таныг унтраах хүртэл"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Дөнгөж сая"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Хөгжүүлэлтэд буй шинэчилсэн график драйверийг ашиглахын тулд аппад нэгдэх"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Утасны чанга яригч"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index cd7f175..8ebb182 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सेव्ह केले"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तुमच्या वापरावर अवलंबून सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकेल (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> पर्यंत टिकावी"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त वेळ शिल्लक आहे (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक वेळी विचारा"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तुम्ही बंद करेपर्यंत"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"आत्ताच"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"अपडेट केलेले ग्राफिक ड्राइव्हर डेव्हलमेंटमध्ये वापरण्यासाठी अॅप निवडा"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनचा स्पीकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index d0b2e12..3593882 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan anda"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tinggal kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Tanya setiap kali"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Sehingga anda matikan"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sebentar tadi"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Sertakan apl untuk menggunakan pemacu grafik yang dikemas kini dalam pembangunan"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Pembesar suara telefon"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 5f5957c..7ac3742 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"သင်၏ အသုံးပြုမှုအပေါ် အခြေခံ၍ <xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်အထိ သုံးနိုင်သည်"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> အထိ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> အောက်သာ ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ကျော် ကျန်သည် (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"အမြဲမေးပါ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"သင်ပိတ်လိုက်သည် အထိ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ယခုလေးတင်"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ဆော့ဖ်ဝဲရေးဆွဲမှုအတွင်း အပ်ဒိတ်လုပ်ထားသော ဂရပ်ဖစ်ဒရိုင်ဗာကို အသုံးပြုရန် အက်ပ်ကို ရွေးချယ်ပါ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ဖုန်းစပီကာ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 3c240f8..ae9c5f2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>, basert på bruken din"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Til <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Spør hver gang"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Til du slår av"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nå nettopp"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Velg app for å bruke en oppdatert grafikkdriver som er under utvikling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefonhøyttaler"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 1699870..0b4510fd 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"सुरक्षित गरियो"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ब्याट्री लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"लगभग <xliff:g id="TIME">%1$s</xliff:g> सम्म टिक्नु पर्छ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> सम्म"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"प्रत्येक पटक सोध्नुहोस्"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"तपाईंले निष्क्रिय नपार्दासम्म"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"अहिले भर्खरै"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"विकासको क्रममा अद्यावधिक गरिएको ग्राफिक ड्राइभर प्रयोग गर्न अप्ट इन गर्नुहोस्"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"फोनको स्पिकर"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 738df94..f227e1c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet zoeken naar netwerken"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> op basis van je gebruik"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> resterend (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Altijd vragen"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Totdat je uitschakelt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Zojuist"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Meld een app aan om het geüpdatete grafische stuurprogramma in ontwikkeling te gebruiken"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefoonluidspreker"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 2d39cc6..bf8493a 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ନେଟ୍‌ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"କିଛି ନାହିଁ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ସେଭ୍‌ ହୋଇଗଲା"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ଅକ୍ଷମ ହୋଇଛି"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ଆପଣଙ୍କର ବ୍ୟବହାରକୁ ଆଧାର କରି ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ବ୍ୟାଟେରୀ ପାଖାପାଖି <xliff:g id="TIME">%1$s</xliff:g> ଚାଲିବ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ବ୍ୟାଟେରୀ <xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ ଚାଲିବ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ରୁ କମ୍ ସମୟ ବଳକା ଅଛି"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ରୁ କମ୍ ସମୟ ବଳକା ଅଛି (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ରୁ ଅଧିକ ସମୟ ବଳକା ଅଛି(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ପ୍ରତ୍ୟେକ ଥର ପଚାରନ୍ତୁ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍‌ ରହିବ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ଡେଭଲପ୍‍‍‍‍‍‍‍‍ମେଣ୍ଟରେ ଅପ୍‍‍‍‍‍‍‍ଡେଟ୍‍ ଗ୍ରାଫିକ୍ସ ଡ୍ରାଇଭର୍‍ ବ୍ୟବହାର କରିବାକୁ ଆପ୍ଟ ଇ‍ନ୍‍ ଅପ୍ଲିକେସନ୍‍"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ଫୋନ୍‍ ସ୍ପିକର୍‍"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 61f0447..f948a7e 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਯੋਗ ਬਣਾਇਆ"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਚੱਲੇਗਾ"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> ਤੱਕ"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ਹਰ ਵਾਰ ਪੁੱਛੋ"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ਹੁਣੇ ਹੀ"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ਅੱਪਡੇਟ ਕੀਤੇ ਵਿਕਾਸ-ਅਧੀਨ ਗ੍ਰਾਫਿਕਸ ਡਰਾਈਵਰ ਦੀ ਵਰਤੋਂ ਕਰਨ ਲਈ ਐਪ ਦੀ ਚੋਣ ਕਰੋ"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ਫ਼ੋਨ ਦਾ ਸਪੀਕਰ"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 519f82c..c75a894 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Na podstawie Twojego sposobu korzystania jeszcze około <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Pozostało ponad: <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Zawsze pytaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dopóki nie wyłączysz"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Przed chwilą"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Wybierz aplikację, która ma używać opracowywanego sterownika grafiki"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Głośnik telefonu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f01ddfa..26e4729 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização."</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca da(s) <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até à(s) <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Resta(m) mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até ser desativado"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora mesmo"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Optar pela aplicação para utilizar a placa gráfica atualizada em desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altifalante do telemóvel"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 16844a2..c273f59 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> com base no seu uso"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Deve durar até cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Até <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s) (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Perguntar sempre"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Até você desativar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Agora"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ative o app para usar o driver gráfico atualizado no desenvolvimento"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Alto-falante do smartphone"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8a7b440..94f4842 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"În baza utilizării, ar trebui să reziste până la aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"a mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Întreabă de fiecare dată"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Până când dezactivați"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Chiar acum"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Aplicația pentru înscriere pentru a folosi driverul actualizat al plăcii grafice este în dezvoltare"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Difuzorul telefonului"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 31274d1..1c331d8 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"При текущем уровне использования заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Заряда (<xliff:g id="LEVEL">%2$s</xliff:g>) хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит менее чем на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит более чем на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Всегда спрашивать"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Пока вы не отключите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Только что"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Приложение будет использовать обновленный драйвер графической системы (на стадии разработки)"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Встроенный динамик"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index b22e068..608ff7f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්‍යාස කිරීම අසාර්ථකයි"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"ඔබේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%1$s</xliff:g> පමන වන තෙක් තිබිය යුතුය"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) පමණ වන තෙක් තිබිය යුතුය"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> පමණ වන තෙක් තිබිය යුතුය"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> දක්වා"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිය (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"සෑම විටම ඉල්ලන්න"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ඔබ ක්‍රියාවිරහිත කරන තුරු"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"මේ දැන්"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"වැඩිදියුණු වෙමින් ඇති යාවත්කාලීන කළ චිත්‍රක ධාවකය භාවිත කිරීමට යෙදුමට ඇතුළු වන්න"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"දුරකථන ස්පීකරය"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 43923b8..2f76ef9 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuté"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g> v závislosti od využitia"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Mal by vydržať približne <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Mal by vydržať približne do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vždy sa opýtať"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokiaľ túto funkciu nevypnete"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Teraz"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prihlásiť aplikáciu, ktorá má používať aktualizovaný ovládač grafickej karty vo vývoji"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Reproduktor telefónu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 6d392fe..64124cb 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Naprava bi morala glede na način uporabe delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Naprava bi morala delovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Preostanek: manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Preostali čas delovanja: manj kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Vedno vprašaj"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Dokler ne izklopite"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pravkar"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Omogočena aplikacija za uporabo posodobljenega grafičnega gonilnika pri razvoju"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Zvočnik telefona"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e05a019..25d575e 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> të mbetura"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mbeten më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mbeten më shumë se <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Pyet çdo herë"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Deri sa ta çaktivizosh"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Pikërisht tani"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Prano aplikacionin për të përdorur drejtuesin e përditësuar të grafikës që është në zhvillim"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Altoparlanti i telefonit"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 371b909..b58b47c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> на основу коришћења"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Преостало је више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -448,4 +450,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Док не искључите"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Управо"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Омогући апликацију за коришћење управљачког програма графичке катице у развоју"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Звучник телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e6872bb..7198b84 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> utifrån din användning"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Till kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Fråga varje gång"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Tills du inaktiverar funktionen"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Nyss"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Välj om appen ska använda den uppdaterade grafikdrivrutinen under utveckling"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Mobilens högtalare"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 23efb91..657b54f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Imezimwa"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g> kulingana na jinsi unavyoitumia"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Inapaswa kudumu kwa takribani <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Inapaswa kudumu hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hadi <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uliza kila wakati"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hadi utakapoizima"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Sasa hivi"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chagua programu itakayotumia kiendeshaji cha michoro kilichosasishwa katika hatua ya kusanidi"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Spika ya simu"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 53ba738..59b42d8 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"நீங்கள் பயன்படுத்துவதன் அடிப்படையில் <xliff:g id="TIME">%1$s</xliff:g> வரை உபயோகிக்க முடியும்"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"<xliff:g id="TIME">%1$s</xliff:g> வரை பயன்படுத்த முடியும்"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> வரை"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும்"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>க்கும் மேல் பயன்படுத்த முடியும் (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ஒவ்வொரு முறையும் கேள்"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"ஆஃப் செய்யும் வரை"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"சற்றுமுன்"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"உருவாக்கத்திலுள்ள புதுப்பிக்கப்பட்ட கிராஃபிக்ஸ் டிரைவரைப் பயன்படுத்த ஆப்ஸைத் தேர்ந்தெடுக்கவும்"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"மொபைல் ஸ்பீக்கர்"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c8c6b4c..af233da 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్‌వర్క్‌ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"మీ వినియోగం ఆధారంగా దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"దాదాపు <xliff:g id="TIME">%1$s</xliff:g> వరకు ఉండాలి"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> వరకు"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ప్రతిసారి అడుగు"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"మీరు ఆఫ్‌ చేసే వరకు"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ఇప్పుడే"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"అభివృద్దిలో అప్‌డేట్‌ చేసిన గ్రాఫిక్ డ్రైవర్‌ను ఉపయోగించడానికి యాప్‌ని ప్రారంభించండి"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ఫోన్ స్పీకర్"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index d293d59..636c3f2 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> เมื่อดูจากการใช้งานของคุณ"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"เหลือเวลาอีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ถามทุกครั้ง"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"จนกว่าคุณจะปิด"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"เมื่อสักครู่"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"เลือกใช้แอปเพื่อใช้ไดรเวอร์กราฟิกที่อัปเดตในการพัฒนา"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"ลำโพงโทรศัพท์"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9da4561..1bcc36a 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Naka-disable"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Magtanong palagi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Hanggang sa i-off mo"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Ngayon lang"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"App sa pag-opt in para magamit ang na-update na graphics driver na ginagawa"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Speaker ng telepono"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 2d5cd7f..ff669f6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kullanımınıza göre saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Saat yaklaşık <xliff:g id="TIME">%1$s</xliff:g> olana kadar kullanılabilmelidir"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Şu saate kadar: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"En fazla <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"En çok <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"En az <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Her zaman sor"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Siz kapatana kadar"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Az önce"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Güncellenmiş grafik sürücüsünü geliştirme ortamında kullanmak için uygulamayı kaydedin"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon hoparlörü"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 512ea86..2ded8c1 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"На основі даних про використання, вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -449,4 +451,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Запитувати щоразу"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Доки ви не вимкнете"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Щойно"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Вибраний додаток, який використовуватиме оновлений графічний драйвер під час розробки"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Динамік телефона"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 84ad3ed..7dc4690 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"‏IP کنفیگریشن کی ناکامی"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> تک بیٹری چلے گی"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> تک"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"ہر بار پوچھیں"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"یہاں تک کہ آپ آف کر دیں"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"ابھی ابھی"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"ڈیولپمنٹ میں اپ ڈیٹ کردہ گرافکس ڈرائیور کو استعمال کرنے کے لیے ایپ آپٹ ان کریں"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"فون اسپیکر"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index af2ada5..5f92c9a 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Hech qanday"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Joriy holatda taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"<xliff:g id="TIME">%1$s</xliff:g> gacha"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan ko‘proq vaqt qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Har safar so‘ralsin"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Bekor qilinmaguncha"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Hozir"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Ilova yangilangan grafik drayverdan (hali ishlov jarayonida) foydalanadi"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Telefon karnayi"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index f454127..eb10fb1 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Sẽ hết pin cho tới khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Cho đến <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Còn lại hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Luôn hỏi"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Cho đến khi bạn tắt"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Vừa xong"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Chọn ứng dụng để sử dụng trình điều khiển đồ họa được cập nhật trong giai đoạn phát triển"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Loa điện thoại"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 10a20be..7b14138 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根据您的使用情况,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>,估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"估计大约还能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"电量剩余使用时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"电量剩余使用时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都询问"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到您将其关闭"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"刚刚"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"为应用启用更新后的显卡驱动,以在开发过程中使用"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手机扬声器"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 1510545..48b6959 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據您的使用情況,電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩餘電量時間少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"還有少於 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"還有超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直至您關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"請選取應用程式,以在開發階段使用更新的顯示卡驅動程式"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 90c0d80..684569b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"根據你的使用情形,預估可持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"目前電量 <xliff:g id="LEVEL">%2$s</xliff:g>,預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"預估還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"每次都詢問"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"直到你關閉為止"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"剛剛"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"選取要在開發階段使用最新版繪圖驅動程式的應用程式"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"手機喇叭"</string>
 </resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f9f4d..d107a5a 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,6 +21,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
+    <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
     <string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
     <string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
     <string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
@@ -374,6 +375,7 @@
     <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> kusukela ekusetshenzisweni kwakho"</string>
     <string name="power_discharge_by" msgid="6453537733650125582">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_discharge_by_only" msgid="107616694963545745">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_only_short" msgid="1372817269546888804">"Kuze kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kusele okungaphansi kunokungu-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Ngaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Ngaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> osele (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -447,4 +449,6 @@
     <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Buza njalo"</string>
     <string name="zen_mode_forever" msgid="2704305038191592967">"Uze uvale isikrini"</string>
     <string name="time_unit_just_now" msgid="6363336622778342422">"Khona manje"</string>
+    <string name="updated_gfx_driver_dev_opt_in_app_summary" msgid="5309913444094165199">"Uhlelo lokusebenza lokukhetha ukungena olungasebenzisa idrayivu yamagrafikhi ekuthuthukiseni"</string>
+    <string name="media_transfer_phone_device_name" msgid="1003823744105758574">"Isipikha sefoni"</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 45a3bb0..9270d13 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -54,18 +54,17 @@
     private final Context mContext;
     private final BluetoothAdapter mLocalAdapter;
     private final LocalBluetoothProfileManager mProfileManager;
+    private final Object mProfileLock = new Object();
     BluetoothDevice mDevice;
     private long mHiSyncId;
     // Need this since there is no method for getting RSSI
     short mRssi;
     // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
     // because current sub device is only for HearingAid and its profile is the same.
-    private final List<LocalBluetoothProfile> mProfiles =
-            Collections.synchronizedList(new ArrayList<>());
+    private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
 
     // List of profiles that were previously in mProfiles, but have been removed
-    private final List<LocalBluetoothProfile> mRemovedProfiles =
-            Collections.synchronizedList(new ArrayList<>());
+    private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
 
     // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
     private boolean mLocalNapRoleConnected;
@@ -74,7 +73,7 @@
 
     private int mMessageRejectionCount;
 
-    private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
+    private final Collection<Callback> mCallbacks = new ArrayList<>();
 
     // How many times user should reject the connection to make the choice persist.
     private final static int MESSAGE_REJECTION_COUNT_LIMIT_TO_PERSIST = 2;
@@ -134,36 +133,42 @@
             }
             return;
         }
-        if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
-            if (profile instanceof MapProfile) {
-                profile.setPreferred(mDevice, true);
-            }
-            if (!mProfiles.contains(profile)) {
-                mRemovedProfiles.remove(profile);
-                mProfiles.add(profile);
-                if (profile instanceof PanProfile &&
-                        ((PanProfile) profile).isLocalRoleNap(mDevice)) {
-                    // Device doesn't support NAP, so remove PanProfile on disconnect
-                    mLocalNapRoleConnected = true;
+
+        synchronized (mProfileLock) {
+            if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
+                if (profile instanceof MapProfile) {
+                    profile.setPreferred(mDevice, true);
                 }
+                if (!mProfiles.contains(profile)) {
+                    mRemovedProfiles.remove(profile);
+                    mProfiles.add(profile);
+                    if (profile instanceof PanProfile
+                            && ((PanProfile) profile).isLocalRoleNap(mDevice)) {
+                        // Device doesn't support NAP, so remove PanProfile on disconnect
+                        mLocalNapRoleConnected = true;
+                    }
+                }
+            } else if (profile instanceof MapProfile
+                    && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+                profile.setPreferred(mDevice, false);
+            } else if (mLocalNapRoleConnected && profile instanceof PanProfile
+                    && ((PanProfile) profile).isLocalRoleNap(mDevice)
+                    && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
+                Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
+                mProfiles.remove(profile);
+                mRemovedProfiles.add(profile);
+                mLocalNapRoleConnected = false;
             }
-        } else if (profile instanceof MapProfile &&
-                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
-            profile.setPreferred(mDevice, false);
-        } else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
-                ((PanProfile) profile).isLocalRoleNap(mDevice) &&
-                newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
-            Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
-            mProfiles.remove(profile);
-            mRemovedProfiles.add(profile);
-            mLocalNapRoleConnected = false;
         }
+
         fetchActiveDevices();
     }
 
     public void disconnect() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            disconnect(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                disconnect(profile);
+            }
         }
         // Disconnect  PBAP server in case its connected
         // This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -203,6 +208,10 @@
         mHiSyncId = id;
     }
 
+    public boolean isHearingAidDevice() {
+        return mHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
+    }
+
     void onBondingDockConnect() {
         // Attempt to connect if UUIDs are available. Otherwise,
         // we will connect when the ACTION_UUID intent arrives.
@@ -210,32 +219,35 @@
     }
 
     private void connectWithoutResettingTimer(boolean connectAllProfiles) {
-        // Try to initialize the profiles if they were not.
-        if (mProfiles.isEmpty()) {
-            // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
-            // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been updated
-            // from bluetooth stack but ACTION.uuid is not sent yet.
-            // Eventually ACTION.uuid will be received which shall trigger the connection of the
-            // various profiles
-            // If UUIDs are not available yet, connect will be happen
-            // upon arrival of the ACTION_UUID intent.
-            Log.d(TAG, "No profiles. Maybe we will connect later");
-            return;
-        }
+        synchronized (mProfileLock) {
+            // Try to initialize the profiles if they were not.
+            if (mProfiles.isEmpty()) {
+                // if mProfiles is empty, then do not invoke updateProfiles. This causes a race
+                // condition with carkits during pairing, wherein RemoteDevice.UUIDs have been
+                // updated from bluetooth stack but ACTION.uuid is not sent yet.
+                // Eventually ACTION.uuid will be received which shall trigger the connection of the
+                // various profiles
+                // If UUIDs are not available yet, connect will be happen
+                // upon arrival of the ACTION_UUID intent.
+                Log.d(TAG, "No profiles. Maybe we will connect later");
+                return;
+            }
 
-        int preferredProfiles = 0;
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
-                if (profile.isPreferred(mDevice)) {
-                    ++preferredProfiles;
-                    connectInt(profile);
+            int preferredProfiles = 0;
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (connectAllProfiles ? profile.accessProfileEnabled()
+                        : profile.isAutoConnectable()) {
+                    if (profile.isPreferred(mDevice)) {
+                        ++preferredProfiles;
+                        connectInt(profile);
+                    }
                 }
             }
-        }
-        if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
+            if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
 
-        if (preferredProfiles == 0) {
-            connectAutoConnectableProfiles();
+            if (preferredProfiles == 0) {
+                connectAutoConnectableProfiles();
+            }
         }
     }
 
@@ -244,10 +256,12 @@
             return;
         }
 
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.isAutoConnectable()) {
-                profile.setPreferred(mDevice, true);
-                connectInt(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (profile.isAutoConnectable()) {
+                    profile.setPreferred(mDevice, true);
+                    connectInt(profile);
+                }
             }
         }
     }
@@ -515,14 +529,16 @@
      * @return Whether it is connected.
      */
     public boolean isConnected() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            int status = getProfileConnectionState(profile);
-            if (status == BluetoothProfile.STATE_CONNECTED) {
-                return true;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                int status = getProfileConnectionState(profile);
+                if (status == BluetoothProfile.STATE_CONNECTED) {
+                    return true;
+                }
             }
-        }
 
-        return false;
+            return false;
+        }
     }
 
     public boolean isConnectedProfile(LocalBluetoothProfile profile) {
@@ -532,14 +548,16 @@
     }
 
     public boolean isBusy() {
-        for (LocalBluetoothProfile profile : mProfiles) {
-            int status = getProfileConnectionState(profile);
-            if (status == BluetoothProfile.STATE_CONNECTING
-                    || status == BluetoothProfile.STATE_DISCONNECTING) {
-                return true;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                int status = getProfileConnectionState(profile);
+                if (status == BluetoothProfile.STATE_CONNECTING
+                        || status == BluetoothProfile.STATE_DISCONNECTING) {
+                    return true;
+                }
             }
+            return getBondState() == BluetoothDevice.BOND_BONDING;
         }
-        return getBondState() == BluetoothDevice.BOND_BONDING;
     }
 
     private boolean updateProfiles() {
@@ -554,8 +572,10 @@
          */
         processPhonebookAccess();
 
-        mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
-                                       mLocalNapRoleConnected, mDevice);
+        synchronized (mProfileLock) {
+            mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles,
+                    mLocalNapRoleConnected, mDevice);
+        }
 
         if (BluetoothUtils.D) {
             Log.e(TAG, "updating profiles for " + mDevice.getAliasName());
@@ -616,7 +636,9 @@
 
     void onBondingStateChanged(int bondState) {
         if (bondState == BluetoothDevice.BOND_NONE) {
-            mProfiles.clear();
+            synchronized (mProfileLock) {
+                mProfiles.clear();
+            }
             mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
             mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN);
@@ -646,9 +668,11 @@
     public List<LocalBluetoothProfile> getConnectableProfiles() {
         List<LocalBluetoothProfile> connectableProfiles =
                 new ArrayList<LocalBluetoothProfile>();
-        for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.accessProfileEnabled()) {
-                connectableProfiles.add(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : mProfiles) {
+                if (profile.accessProfileEnabled()) {
+                    connectableProfiles.add(profile);
+                }
             }
         }
         return connectableProfiles;
@@ -823,10 +847,12 @@
 
     public int getMaxConnectionState() {
         int maxState = BluetoothProfile.STATE_DISCONNECTED;
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
-            if (connectionStatus > maxState) {
-                maxState = connectionStatus;
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
+                if (connectionStatus > maxState) {
+                    maxState = connectionStatus;
+                }
             }
         }
         return maxState;
@@ -843,32 +869,34 @@
         boolean hfpConnected = true;         // HFP is connected
         boolean hearingAidConnected = true;  // Hearing Aid is connected
 
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
 
-            switch (connectionStatus) {
-                case BluetoothProfile.STATE_CONNECTING:
-                case BluetoothProfile.STATE_DISCONNECTING:
-                    return mContext.getString(
-                            BluetoothUtils.getConnectionStateSummary(connectionStatus));
+                switch (connectionStatus) {
+                    case BluetoothProfile.STATE_CONNECTING:
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        return mContext.getString(
+                                BluetoothUtils.getConnectionStateSummary(connectionStatus));
 
-                case BluetoothProfile.STATE_CONNECTED:
-                    profileConnected = true;
-                    break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        profileConnected = true;
+                        break;
 
-                case BluetoothProfile.STATE_DISCONNECTED:
-                    if (profile.isProfileReady()) {
-                        if ((profile instanceof A2dpProfile) ||
-                                (profile instanceof A2dpSinkProfile)) {
-                            a2dpConnected = false;
-                        } else if ((profile instanceof HeadsetProfile) ||
-                                (profile instanceof HfpClientProfile)) {
-                            hfpConnected = false;
-                        } else if (profile instanceof HearingAidProfile) {
-                            hearingAidConnected = false;
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        if (profile.isProfileReady()) {
+                            if (profile instanceof A2dpProfile
+                                    || profile instanceof A2dpSinkProfile) {
+                                a2dpConnected = false;
+                            } else if (profile instanceof HeadsetProfile
+                                    || profile instanceof HfpClientProfile) {
+                                hfpConnected = false;
+                            } else if (profile instanceof HearingAidProfile) {
+                                hearingAidConnected = false;
+                            }
                         }
-                    }
-                    break;
+                        break;
+                }
             }
         }
 
@@ -924,32 +952,34 @@
         boolean hfpNotConnected = false;        // HFP is preferred but not connected
         boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
 
-        for (LocalBluetoothProfile profile : getProfiles()) {
-            int connectionStatus = getProfileConnectionState(profile);
+        synchronized (mProfileLock) {
+            for (LocalBluetoothProfile profile : getProfiles()) {
+                int connectionStatus = getProfileConnectionState(profile);
 
-            switch (connectionStatus) {
-                case BluetoothProfile.STATE_CONNECTING:
-                case BluetoothProfile.STATE_DISCONNECTING:
-                    return mContext.getString(
-                            BluetoothUtils.getConnectionStateSummary(connectionStatus));
+                switch (connectionStatus) {
+                    case BluetoothProfile.STATE_CONNECTING:
+                    case BluetoothProfile.STATE_DISCONNECTING:
+                        return mContext.getString(
+                                BluetoothUtils.getConnectionStateSummary(connectionStatus));
 
-                case BluetoothProfile.STATE_CONNECTED:
-                    profileConnected = true;
-                    break;
+                    case BluetoothProfile.STATE_CONNECTED:
+                        profileConnected = true;
+                        break;
 
-                case BluetoothProfile.STATE_DISCONNECTED:
-                    if (profile.isProfileReady()) {
-                        if ((profile instanceof A2dpProfile) ||
-                                (profile instanceof A2dpSinkProfile)){
-                            a2dpNotConnected = true;
-                        } else if ((profile instanceof HeadsetProfile) ||
-                                (profile instanceof HfpClientProfile)) {
-                            hfpNotConnected = true;
-                        } else if (profile instanceof  HearingAidProfile) {
-                            hearingAidNotConnected = true;
+                    case BluetoothProfile.STATE_DISCONNECTED:
+                        if (profile.isProfileReady()) {
+                            if (profile instanceof A2dpProfile
+                                    || profile instanceof A2dpSinkProfile) {
+                                a2dpNotConnected = true;
+                            } else if (profile instanceof HeadsetProfile
+                                    || profile instanceof HfpClientProfile) {
+                                hfpNotConnected = true;
+                            } else if (profile instanceof HearingAidProfile) {
+                                hearingAidNotConnected = true;
+                            }
                         }
-                    }
-                    break;
+                        break;
+                }
             }
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index f7f6589..3a62838 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -50,7 +50,7 @@
     }
 
     public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
-        return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
+        return new ArrayList<>(mCachedDevices);
     }
 
     public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 92fd868..12b8efb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -53,6 +53,8 @@
             "com.android.settings.category.ia.night_display";
     public static final String CATEGORY_PRIVACY =
             "com.android.settings.category.ia.privacy";
+    public static final String CATEGORY_ENTERPRISE_PRIVACY =
+            "com.android.settings.category.ia.enterprise_privacy";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
deleted file mode 100644
index e9c5238..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2018 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.net;
-
-import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.SET_FOREGROUND;
-import static android.net.NetworkStats.TAG_NONE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.text.format.DateUtils.HOUR_IN_MILLIS;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-import com.android.settingslib.AppItem;
-
-/**
- * Loader for historical chart data for both network and UID details.
- *
- * Deprecated in favor of {@link NetworkCycleChartDataLoader} and
- * {@link NetworkCycleDataForUidLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class ChartDataLoaderCompat extends AsyncTaskLoader<ChartData> {
-    private static final String KEY_TEMPLATE = "template";
-    private static final String KEY_APP = "app";
-    private static final String KEY_FIELDS = "fields";
-
-    private final INetworkStatsSession mSession;
-    private final Bundle mArgs;
-
-    public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
-        return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
-    }
-
-    public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
-        final Bundle args = new Bundle();
-        args.putParcelable(KEY_TEMPLATE, template);
-        args.putParcelable(KEY_APP, app);
-        args.putInt(KEY_FIELDS, fields);
-        return args;
-    }
-
-    public ChartDataLoaderCompat(Context context, INetworkStatsSession session, Bundle args) {
-        super(context);
-        mSession = session;
-        mArgs = args;
-    }
-
-    @Override
-    protected void onStartLoading() {
-        super.onStartLoading();
-        forceLoad();
-    }
-
-    @Override
-    public ChartData loadInBackground() {
-        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
-        final AppItem app = mArgs.getParcelable(KEY_APP);
-        final int fields = mArgs.getInt(KEY_FIELDS);
-
-        try {
-            return loadInBackground(template, app, fields);
-        } catch (RemoteException e) {
-            // since we can't do much without history, and we don't want to
-            // leave with half-baked UI, we bail hard.
-            throw new RuntimeException("problem reading network stats", e);
-        }
-    }
-
-    private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
-            throws RemoteException {
-        final ChartData data = new ChartData();
-        data.network = mSession.getHistoryForNetwork(template, fields);
-
-        if (app != null) {
-            // load stats for current uid and template
-            final int size = app.uids.size();
-            for (int i = 0; i < size; i++) {
-                final int uid = app.uids.keyAt(i);
-                data.detailDefault = collectHistoryForUid(
-                        template, uid, SET_DEFAULT, data.detailDefault);
-                data.detailForeground = collectHistoryForUid(
-                        template, uid, SET_FOREGROUND, data.detailForeground);
-            }
-
-            if (size > 0) {
-                data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
-                data.detail.recordEntireHistory(data.detailDefault);
-                data.detail.recordEntireHistory(data.detailForeground);
-            } else {
-                data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
-                data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
-            }
-        }
-
-        return data;
-    }
-
-    @Override
-    protected void onStopLoading() {
-        super.onStopLoading();
-        cancelLoad();
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        cancelLoad();
-    }
-
-    /**
-     * Collect {@link NetworkStatsHistory} for the requested UID, combining with
-     * an existing {@link NetworkStatsHistory} if provided.
-     */
-    private NetworkStatsHistory collectHistoryForUid(
-            NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
-            throws RemoteException {
-        final NetworkStatsHistory history = mSession.getHistoryForUid(
-                template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
-
-        if (existing != null) {
-            existing.recordEntireHistory(history);
-            return existing;
-        } else {
-            return history;
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 183d485..180b77e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -32,30 +32,24 @@
 import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Range;
 
 import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 
 import java.time.ZonedDateTime;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.Locale;
 
 public class DataUsageController {
 
     private static final String TAG = "DataUsageController";
-    @VisibleForTesting
-    static final String DATA_USAGE_V2 = "settings_data_usage_v2";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
@@ -95,21 +89,6 @@
                 * mContext.getResources().getInteger(R.integer.default_data_warning_level_mb);
     }
 
-    @VisibleForTesting
-    @Deprecated
-    INetworkStatsSession getSession() {
-        if (mSession == null) {
-            try {
-                mSession = mStatsService.openSession();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to open stats session", e);
-            } catch (RuntimeException e) {
-                Log.w(TAG, "Failed to open stats session", e);
-            }
-        }
-        return mSession;
-    }
-
     public void setCallback(Callback callback) {
         mCallback = callback;
     }
@@ -149,13 +128,7 @@
             end = now;
             start = now - DateUtils.WEEK_IN_MILLIS * 4;
         }
-        final long totalBytes;
-        final long callStart = System.currentTimeMillis();
-        if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
-            totalBytes = getUsageLevel(template, start, end);
-        } else {
-            totalBytes = getUsageLevel(template, start, end, now);
-        }
+        final long totalBytes = getUsageLevel(template, start, end);
         if (totalBytes < 0L) {
             return warn("no entry data");
         }
@@ -185,32 +158,7 @@
      * retrieving the data.
      */
     public long getHistoricalUsageLevel(NetworkTemplate template) {
-        if (FeatureFlagUtils.isEnabled(mContext, DATA_USAGE_V2)) {
-            return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
-        } else {
-            final long now = System.currentTimeMillis();
-            return getUsageLevel(template, 0L /* start */, now /* end */, now);
-        }
-    }
-
-    @Deprecated
-    private long getUsageLevel(NetworkTemplate template, long start, long end, long now) {
-        final INetworkStatsSession session = getSession();
-        if (session != null) {
-            try {
-                final NetworkStatsHistory history =
-                    session.getHistoryForNetwork(template, FIELDS);
-                final NetworkStatsHistory.Entry entry = history.getValues(
-                        start, end, System.currentTimeMillis() /* now */, null /* recycle */);
-                if (entry != null) {
-                    return entry.rxBytes + entry.txBytes;
-                }
-                Log.w(TAG, "Failed to get data usage, no entry data");
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to get data usage, remote call failed");
-            }
-        }
-        return -1L;
+        return getUsageLevel(template, 0L /* start */, System.currentTimeMillis() /* end */);
     }
 
     private long getUsageLevel(NetworkTemplate template, long start, long end) {
@@ -241,20 +189,6 @@
         return null;
     }
 
-    @Deprecated
-    private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
-        return entry == null ? null : new StringBuilder("Entry[")
-                .append("bucketDuration=").append(entry.bucketDuration)
-                .append(",bucketStart=").append(entry.bucketStart)
-                .append(",activeTime=").append(entry.activeTime)
-                .append(",rxBytes=").append(entry.rxBytes)
-                .append(",rxPackets=").append(entry.rxPackets)
-                .append(",txBytes=").append(entry.txBytes)
-                .append(",txPackets=").append(entry.txPackets)
-                .append(",operations=").append(entry.operations)
-                .append(']').toString();
-    }
-
     private static String statsBucketToString(Bucket bucket) {
         return bucket == null ? null : new StringBuilder("Entry[")
             .append("bucketDuration=").append(bucket.getEndTimeStamp() - bucket.getStartTimeStamp())
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
deleted file mode 100644
index 82bb011..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoaderCompat.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2018 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.net;
-
-import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import androidx.loader.content.AsyncTaskLoader;
-
-/**
- * Deprecated in favor of {@link NetworkStatsDetailLoader}
- *
- * @deprecated
- */
-@Deprecated
-public class SummaryForAllUidLoaderCompat extends AsyncTaskLoader<NetworkStats> {
-    private static final String KEY_TEMPLATE = "template";
-    private static final String KEY_START = "start";
-    private static final String KEY_END = "end";
-
-    private final INetworkStatsSession mSession;
-    private final Bundle mArgs;
-
-    public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
-        final Bundle args = new Bundle();
-        args.putParcelable(KEY_TEMPLATE, template);
-        args.putLong(KEY_START, start);
-        args.putLong(KEY_END, end);
-        return args;
-    }
-
-    public SummaryForAllUidLoaderCompat(Context context, INetworkStatsSession session,
-            Bundle args) {
-        super(context);
-        mSession = session;
-        mArgs = args;
-    }
-
-    @Override
-    protected void onStartLoading() {
-        super.onStartLoading();
-        forceLoad();
-    }
-
-    @Override
-    public NetworkStats loadInBackground() {
-        final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
-        final long start = mArgs.getLong(KEY_START);
-        final long end = mArgs.getLong(KEY_END);
-
-        try {
-            return mSession.getSummaryForAllUid(template, start, end, false);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    @Override
-    protected void onStopLoading() {
-        super.onStopLoading();
-        cancelLoad();
-    }
-
-    @Override
-    protected void onReset() {
-        super.onReset();
-        cancelLoad();
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index d15a3ef..cfa067f 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -32,12 +32,13 @@
 
 include $(BUILD_PACKAGE)
 
-#############################################
-# SettingsLib Robolectric test target.      #
-#############################################
+############################################################
+# SettingsLib Robolectric test target.                     #
+############################################################
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := SettingsLibRoboTests
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -53,6 +54,9 @@
 
 LOCAL_MODULE_TAGS := optional
 
+# Generate test_config.properties
+include external/robolectric-shadows/gen_test_config.mk
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 #############################################################
diff --git a/packages/SettingsLib/tests/robotests/config/robolectric.properties b/packages/SettingsLib/tests/robotests/config/robolectric.properties
index 6b5b8e5..fab7251 100644
--- a/packages/SettingsLib/tests/robotests/config/robolectric.properties
+++ b/packages/SettingsLib/tests/robotests/config/robolectric.properties
@@ -1,5 +1 @@
-manifest=frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml
 sdk=NEWEST_SDK
-
-shadows=\
-   com.android.settingslib.testutils.shadow.ShadowXmlUtils
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
index 9ba9967..3a4e2e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceComaptTest.java
@@ -30,10 +30,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.util.ReflectionHelpers;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CustomEditTextPreferenceComaptTest {
 
     @Mock
@@ -70,7 +71,7 @@
     }
 
     private static class TestPreference extends CustomEditTextPreferenceCompat {
-        public TestPreference(Context context) {
+        private TestPreference(Context context) {
             super(context);
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
index 9d7f59a..e94a06c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/CustomEditTextPreferenceTest.java
@@ -30,10 +30,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.util.ReflectionHelpers;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CustomEditTextPreferenceTest {
 
     @Mock
@@ -70,7 +71,7 @@
     }
 
     private static class TestPreference extends CustomEditTextPreference {
-        public TestPreference(Context context) {
+        private TestPreference(Context context) {
             super(context);
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
index 19a916c..4e8af73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/DeviceInfoUtilsTest.java
@@ -24,9 +24,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DeviceInfoUtilsTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
index 36b70df..4d76331 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/HelpUtilsTest.java
@@ -18,12 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.R;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
@@ -36,20 +37,19 @@
 import android.provider.Settings;
 import android.view.MenuItem;
 
-import android.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 /**
  * Tests for {@link HelpUtils}.
  */
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class HelpUtilsTest {
     private static final String TEST_HELP_URL = "intent:#Intent;action=com.android.test;end";
     private static final String PACKAGE_NAME_KEY = "package-name-key";
@@ -83,8 +83,6 @@
         when(mContext.getResources().getString(R.string.config_feedbackIntentNameKey))
                 .thenReturn(FEEDBACK_INTENT_NAME_KEY);
         when(mActivity.getPackageManager()).thenReturn(mPackageManager);
-
-
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 88ac8ce..2b5a4e0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -25,8 +25,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
@@ -44,11 +44,12 @@
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.Collections;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class RestrictedLockUtilsTest {
 
     @Mock
@@ -178,8 +179,7 @@
     public void checkIfKeyguardFeaturesAreDisabled_doesMatchAllowedFeature_unifiedManagedProfile() {
         UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
         UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
-        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
-                userInfo, profileInfo}));
+        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
 
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
                 .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -207,8 +207,7 @@
     public void checkIfKeyguardFeaturesAreDisabled_notMatchOtherFeatures_unifiedManagedProfile() {
         UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
         UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
-        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
-                userInfo, profileInfo}));
+        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
 
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
                 .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -231,8 +230,7 @@
     public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesProfile_separateManagedProfile() {
         UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
         UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
-        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
-                userInfo, profileInfo}));
+        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
 
         when(mDevicePolicyManager.getKeyguardDisabledFeatures(mAdmin1, mUserId))
                 .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE);
@@ -268,8 +266,7 @@
     public void checkIfKeyguardFeaturesAreDisabled_onlyMatchesParent_profileParentPolicy() {
         UserInfo userInfo = setUpUser(mUserId, new ComponentName[] {mAdmin1});
         UserInfo profileInfo = setUpManagedProfile(mProfileId, new ComponentName[] {mAdmin2});
-        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(new UserInfo[] {
-                userInfo, profileInfo}));
+        when(mUserManager.getProfiles(mUserId)).thenReturn(Arrays.asList(userInfo, profileInfo));
 
         when(mProxy.getParentProfileInstance(any(DevicePolicyManager.class), any())
                 .getKeyguardDisabledFeatures(mAdmin2, mProfileId))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
index 79d682d6..1b10c73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedPreferenceHelperTest.java
@@ -16,7 +16,6 @@
 
 package com.android.settingslib;
 
-
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -35,8 +34,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class RestrictedPreferenceHelperTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
deleted file mode 100644
index 8757eed..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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;
-
-import android.annotation.NonNull;
-
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
-import org.robolectric.manifest.AndroidManifest;
-import org.robolectric.res.Fs;
-import org.robolectric.res.ResourcePath;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.List;
-
-public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner {
-
-    public SettingsLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
-        super(testClass);
-    }
-
-    /**
-     * We are going to create our own custom manifest so we can add multiple resource paths to it.
-     */
-    @Override
-    protected AndroidManifest getAppManifest(Config config) {
-        try {
-            // Using the manifest file's relative path, we can figure out the application directory.
-            final URL appRoot =
-                new URL("file:frameworks/base/packages/SettingsLib/tests/robotests");
-            final URL manifestPath = new URL(appRoot, "AndroidManifest.xml");
-            final URL resDir = new URL(appRoot, "res");
-            final URL assetsDir = new URL(appRoot, "assets");
-
-            return new AndroidManifest(Fs.fromURL(manifestPath), Fs.fromURL(resDir),
-                Fs.fromURL(assetsDir), "com.android.settingslib") {
-                @Override
-                public List<ResourcePath> getIncludedResourcePaths() {
-                    final List<ResourcePath> paths = super.getIncludedResourcePaths();
-                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/AppPreference/res"));
-                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/HelpUtils/res"));
-                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/RestrictedLockUtils/res"));
-                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/"
-                            + "SettingsLayoutPreference/res"));
-                    paths.add(resourcePath("file:frameworks/base/packages/SettingsLib/res"));
-                    paths.add(resourcePath("file:frameworks/base/core/res/res"));
-                    paths.add(resourcePath("file:out/soong/.intermediates/prebuilts/sdk/current/androidx/androidx.appcompat_appcompat-nodeps/android_common/aar/res/"));
-                    return paths;
-                }
-            };
-        } catch (MalformedURLException e) {
-            throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
-        }
-    }
-
-    private static ResourcePath resourcePath(@NonNull String spec) {
-        try {
-            return new ResourcePath(null, Fs.fromURL(new URL(spec)), null);
-        } catch (MalformedURLException e) {
-            throw new RuntimeException("SettingsLibRobolectricTestRunner failure", e);
-        }
-    }
-}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
index e70baa1..0ca7791 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TetherUtilTest.java
@@ -32,12 +32,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class TetherUtilTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index c0b69f2..3f0ba13 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -36,9 +36,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class TwoTargetPreferenceTest {
 
     private PreferenceViewHolder mViewHolder;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 08a75ab..594d767 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -49,6 +49,7 @@
 import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
@@ -58,10 +59,8 @@
 import java.util.HashMap;
 import java.util.Map;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {
-            UtilsTest.ShadowSecure.class,
-            UtilsTest.ShadowLocationManager.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
     private static final String PERCENTAGE_0 = "0%";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
index 152d024..44fdaec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/accessibility/AccessibilityUtilsTest.java
@@ -23,14 +23,13 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class AccessibilityUtilsTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
index b307b47..ccec175a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java
@@ -41,7 +41,6 @@
 import android.os.UserHandle;
 import android.util.IconDrawableFactory;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.applications.ApplicationsState.AppEntry;
 import com.android.settingslib.applications.ApplicationsState.Callbacks;
 import com.android.settingslib.applications.ApplicationsState.Session;
@@ -55,6 +54,7 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
@@ -67,7 +67,7 @@
 import java.util.List;
 import java.util.UUID;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowUserManager.class,
         ApplicationsStateRoboTest.ShadowIconDrawableFactory.class,
         ApplicationsStateRoboTest.ShadowPackageManager.class})
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
index a92a2dd..50fad70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -32,16 +32,15 @@
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DefaultAppInfoTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
index d8c459c..f7fd25b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ServiceListingTest.java
@@ -26,14 +26,13 @@
 import android.content.ComponentName;
 import android.provider.Settings;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class ServiceListingTest {
 
     private static final String TEST_SETTING = "testSetting";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index 29831a8..c555cbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -17,8 +17,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -32,7 +32,6 @@
 import android.content.res.Resources;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -40,26 +39,27 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class A2dpProfileTest {
 
     @Mock
-    Context mContext;
+    private Context mContext;
     @Mock
-    CachedBluetoothDeviceManager mDeviceManager;
+    private CachedBluetoothDeviceManager mDeviceManager;
     @Mock
-    LocalBluetoothProfileManager mProfileManager;
+    private LocalBluetoothProfileManager mProfileManager;
     @Mock
-    BluetoothDevice mDevice;
+    private BluetoothDevice mDevice;
     @Mock
-    BluetoothA2dp mBluetoothA2dp;
-    BluetoothProfile.ServiceListener mServiceListener;
+    private BluetoothA2dp mBluetoothA2dp;
+    private BluetoothProfile.ServiceListener mServiceListener;
 
-    A2dpProfile mProfile;
+    private A2dpProfile mProfile;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
 
     @Before
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
index 274fff8..976445e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothA2dpSink;
 import android.bluetooth.BluetoothProfile;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class A2dpSinkProfileTest {
 
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothA2dpSink mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private A2dpSinkProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index c147d5e..27b8dfc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -29,20 +29,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Handler;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BluetoothEventManagerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 07310bd..0eb6de9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -21,14 +21,14 @@
 import android.graphics.drawable.Drawable;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BluetoothUtilsTest {
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 9c75491..47b1210 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -28,18 +28,17 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.Collection;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CachedBluetoothDeviceManagerTest {
     private final static String DEVICE_NAME_1 = "TestName_1";
     private final static String DEVICE_NAME_2 = "TestName_2";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5ceede1..4e5d38a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -31,16 +31,15 @@
 import android.content.Context;
 import android.media.AudioManager;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CachedBluetoothDeviceTest {
     private final static String DEVICE_NAME = "TestName";
     private final static String DEVICE_ALIAS = "TestAlias";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
index c0a1f0c..9adef82 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -11,7 +11,6 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -19,11 +18,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class HeadsetProfileTest {
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index cb1b12d..2b5466c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -29,16 +29,15 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class HearingAidDeviceManagerTest {
     private final static long HISYNCID1 = 10;
     private final static long HISYNCID2 = 11;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
index 187be0b..69c020d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothProfile;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class HfpClientProfileTest {
 
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothHeadsetClient mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private HfpClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
index c91ee22..f38af70 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHidDevice;
 import android.bluetooth.BluetoothProfile;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class HidDeviceProfileTest {
 
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothHidDevice mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private HidDeviceProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index a3c3a54..5d5872e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,7 +37,6 @@
 import android.content.Intent;
 import android.os.ParcelUuid;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -45,6 +44,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
@@ -52,7 +52,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class LocalBluetoothProfileManagerTest {
     private final static long HISYNCID = 10;
@@ -270,13 +270,13 @@
         verify(mCachedBluetoothDevice).refresh();
     }
 
-    private List<Integer> generateList(int[] profile) {
-        if (profile == null) {
+    private List<Integer> generateList(int[] profiles) {
+        if (profiles == null) {
             return null;
         }
-        final List<Integer> profileList = new ArrayList<>(profile.length);
-        for(int i = 0; i < profile.length; i++) {
-            profileList.add(profile[i]);
+        final List<Integer> profileList = new ArrayList<>(profiles.length);
+        for (int profile : profiles) {
+            profileList.add(profile);
         }
         return profileList;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
index c4c48a8..6f66709 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothMapClient;
 import android.bluetooth.BluetoothProfile;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class MapClientProfileTest {
 
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothMapClient mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private MapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
index e4a444c..b21ec9c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothPbapClient;
 import android.bluetooth.BluetoothProfile;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,12 +33,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(shadows = {ShadowBluetoothAdapter.class})
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowBluetoothAdapter.class)
 public class PbapClientProfileTest {
 
     @Mock
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothPbapClient mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private PbapClientProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
index 9bb53ee..ec88034 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -18,18 +18,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothSap;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
@@ -37,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class SapProfileTest {
 
@@ -52,8 +49,6 @@
     @Mock
     private BluetoothSap mService;
     @Mock
-    private CachedBluetoothDevice mCachedBluetoothDevice;
-    @Mock
     private BluetoothDevice mBluetoothDevice;
     private BluetoothProfile.ServiceListener mServiceListener;
     private SapProfile mProfile;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index 4d7553c..28de191 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -24,16 +24,15 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class AbstractPreferenceControllerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 4ec6fb2..8a0ae91 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -27,7 +27,6 @@
 import android.content.Intent;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -35,13 +34,14 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.util.ReflectionHelpers;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class MetricsFeatureProviderTest {
     @Mock
     private LogWriter mLogWriter;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
index 6285fcd..8f51dec 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SharedPreferenceLoggerTest.java
@@ -17,8 +17,8 @@
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_SETTINGS_PREFERENCE_CHANGE;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -26,16 +26,15 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class SharedPreferenceLoggerTest {
 
     private static final String TEST_TAG = "tag";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
index b251c09..097db17 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixinTest.java
@@ -17,10 +17,10 @@
 
 import static com.android.settingslib.core.instrumentation.Instrumentable.METRICS_CATEGORY_UNKNOWN;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -35,7 +35,6 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,10 +42,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.android.controller.ActivityController;
 
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class VisibilityLoggerMixinTest {
 
     @Mock
@@ -139,7 +138,7 @@
 
     private final class TestInstrumentable implements Instrumentable {
 
-        public static final int TEST_METRIC = 12345;
+        private static final int TEST_METRIC = 12345;
 
         @Override
         public int getMetricsCategory() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index 887c1d5..29e37e4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -28,7 +28,6 @@
 
 import androidx.lifecycle.LifecycleOwner;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.events.OnAttach;
 import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
@@ -43,10 +42,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.android.controller.ActivityController;
 import org.robolectric.shadows.androidx.fragment.FragmentController;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LifecycleTest {
 
     private LifecycleOwner mLifecycleOwner;
@@ -56,7 +56,7 @@
 
         final TestObserver mFragObserver;
 
-        public TestDialogFragment() {
+        private TestDialogFragment() {
             mFragObserver = new TestObserver();
             mLifecycle.addObserver(mFragObserver);
         }
@@ -236,11 +236,11 @@
     }
 
     private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected {
-        public boolean wasCalled = false;
+        private boolean mWasCalled = false;
 
         @Override
         public boolean onOptionsItemSelected(MenuItem menuItem) {
-            wasCalled = true;
+            mWasCalled = true;
             return false;
         }
     }
@@ -258,14 +258,14 @@
         fragment.onPrepareOptionsMenu(null);
         fragment.onOptionsItemSelected(null);
 
-        assertThat(accepter.wasCalled).isFalse();
+        assertThat(accepter.mWasCalled).isFalse();
     }
 
     private class OnStartObserver implements LifecycleObserver, OnStart {
 
         private final Lifecycle mLifecycle;
 
-        public OnStartObserver(Lifecycle lifecycle) {
+        private OnStartObserver(Lifecycle lifecycle) {
             mLifecycle = lifecycle;
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
index 9dd93b3a..6191a00 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DeveloperOptionsPreferenceControllerTest.java
@@ -22,16 +22,15 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DeveloperOptionsPreferenceControllerTest {
 
     private static final String TEST_KEY = "Test_pref_key";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index a0fa6b5..3475ff7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -18,23 +18,19 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.os.UserManager;
 import android.content.Context;
+import android.os.UserManager;
 import android.provider.Settings;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadow.api.Shadow;
-
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DevelopmentSettingsEnablerTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index d7b23b0..e84a25c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -32,17 +32,16 @@
 import androidx.preference.PreferenceScreen;
 import androidx.preference.SwitchPreference;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.shadows.ShadowApplication;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class EnableAdbPreferenceControllerTest {
     @Mock(answer = RETURNS_DEEP_STUBS)
     private PreferenceScreen mScreen;
@@ -150,7 +149,7 @@
     }
 
     class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
-        public ConcreteEnableAdbPreferenceController(Context context) {
+        private ConcreteEnableAdbPreferenceController(Context context) {
             super(context);
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 2f78899..146be23 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -45,16 +45,16 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LogdSizePreferenceControllerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
index ed128e0..d5afb4b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -29,7 +29,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -37,9 +36,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LogpersistPreferenceControllerTest {
 
     private LifecycleOwner mLifecycleOwner;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
index 40db478..d1212fc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropPokerTest.java
@@ -27,16 +27,15 @@
 import android.os.IBinder;
 import android.os.Parcel;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class SystemPropPokerTest {
 
     @Spy
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
index 234b4d5..16de5f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
@@ -26,7 +26,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -34,11 +33,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BluetoothAddressPreferenceControllerTest {
     @Mock
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
index aee956c..4444e63 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -30,7 +30,6 @@
 import android.content.IntentFilter;
 import android.os.Handler;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -39,8 +38,9 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class ConnectivityPreferenceControllerTest {
     @Mock
     private Context mContext;
@@ -91,8 +91,7 @@
     private static class ConcreteConnectivityPreferenceController
             extends AbstractConnectivityPreferenceController {
 
-
-        public ConcreteConnectivityPreferenceController(Context context,
+        private ConcreteConnectivityPreferenceController(Context context,
                 Lifecycle lifecycle) {
             super(context, lifecycle);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
index 2b490ee..bd223bd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -25,12 +25,10 @@
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.SubscriptionManager;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -38,11 +36,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowSubscriptionManager;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class ImsStatusPreferenceControllerTest {
     @Mock
     private Context mContext;
@@ -61,8 +58,9 @@
     }
 
     @Test
-    @Config(shadows = ShadowSubscriptionManager.class)
     public void testIsAvailable() {
+        ShadowSubscriptionManager.setDefaultDataSubscriptionId(1234);
+
         CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
         doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
 
@@ -92,18 +90,10 @@
                 .that(imsStatusPreferenceController.isAvailable()).isFalse();
     }
 
-    @Implements(SubscriptionManager.class)
-    public static class ShadowSubscriptionManager {
-        @Implementation
-        public static int getDefaultDataSubscriptionId() {
-            return 1234;
-        }
-    }
-
     private static class ConcreteImsStatusPreferenceController
             extends AbstractImsStatusPreferenceController {
 
-        public ConcreteImsStatusPreferenceController(Context context,
+        private ConcreteImsStatusPreferenceController(Context context,
                 Lifecycle lifecycle) {
             super(context, lifecycle);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index 1d957c3..76a26d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -27,7 +27,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -35,11 +34,12 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class IpAddressPreferenceControllerTest {
     @Mock
     private Context mContext;
@@ -75,8 +75,7 @@
     private static class ConcreteIpAddressPreferenceController extends
             AbstractIpAddressPreferenceController {
 
-        public ConcreteIpAddressPreferenceController(Context context,
-                Lifecycle lifecycle) {
+        private ConcreteIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
             super(context, lifecycle);
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index dc77400..5b71bdd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -25,16 +25,15 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class SerialNumberPreferenceControllerTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
index eb77cb6..5252c6c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SimStatusImeiInfoPreferenceControllerTest.java
@@ -24,17 +24,16 @@
 import android.os.UserManager;
 import android.util.SparseBooleanArray;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {SimStatusImeiInfoPreferenceControllerTest.ShadowUserManager.class,
                 SimStatusImeiInfoPreferenceControllerTest.ShadowConnectivityManager.class})
 public class SimStatusImeiInfoPreferenceControllerTest {
@@ -106,7 +105,7 @@
 
         private final SparseBooleanArray mSupportedNetworkTypes = new SparseBooleanArray();
 
-        public void setNetworkSupported(int networkType, boolean supported) {
+        private void setNetworkSupported(int networkType, boolean supported) {
             mSupportedNetworkTypes.put(networkType, supported);
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
index 2e0348d..f09879b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -28,7 +28,6 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -36,9 +35,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowLooper;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class UptimePreferenceControllerTest {
     @Mock
     private Context mContext;
@@ -92,7 +92,7 @@
 
     private static class ConcreteUptimePreferenceController
             extends AbstractUptimePreferenceController {
-        public ConcreteUptimePreferenceController(Context context,
+        private ConcreteUptimePreferenceController(Context context,
                 Lifecycle lifecycle) {
             super(context, lifecycle);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 359ea77..74e5bf5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -33,7 +33,6 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -41,13 +40,14 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.Arrays;
 import java.util.List;
 
 @SuppressLint("HardwareIds")
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class WifiMacAddressPreferenceControllerTest {
     @Mock
     private Lifecycle mLifecycle;
@@ -197,7 +197,7 @@
     private static class ConcreteWifiMacAddressPreferenceController
             extends AbstractWifiMacAddressPreferenceController {
 
-        public ConcreteWifiMacAddressPreferenceController(Context context,
+        private ConcreteWifiMacAddressPreferenceController(Context context,
                 Lifecycle lifecycle) {
             super(context, lifecycle);
         }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
index ca621ca..c0924d9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
@@ -20,12 +20,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BrightnessUtilsTest {
 
     private static final int MIN = 1;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 59a3dd6..605c861 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -20,14 +20,13 @@
 
 import android.util.ArraySet;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Set;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class CategoryKeyTest {
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
index 40e7386..b77670b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileTest.java
@@ -13,15 +13,14 @@
 import android.os.Bundle;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class TileTest {
 
     private ActivityInfo mActivityInfo;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 362ae4c..bbb4249 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -54,7 +54,6 @@
 import android.util.Pair;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,12 +61,13 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class TileUtilsTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index d0b6dab..2988905 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -33,28 +33,26 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BatterySaverUtilsTest {
-    final int BATTERY_SAVER_THRESHOLD_1 = 15;
-    final int BATTERY_SAVER_THRESHOLD_2 = 20;
+    private static final int BATTERY_SAVER_THRESHOLD_1 = 15;
+    private static final int BATTERY_SAVER_THRESHOLD_2 = 20;
 
     @Mock
-    Context mMockContext;
+    private Context mMockContext;
 
     @Mock
-    ContentResolver mMockResolver;
+    private ContentResolver mMockResolver;
 
     @Mock
-    PowerManager mMockPowerManager;
+    private PowerManager mMockPowerManager;
 
     @Before
     public void setUp() throws Exception {
@@ -66,11 +64,11 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_enable_firstCall_needWarning() throws Exception {
+    public void testSetPowerSaveMode_enable_firstCall_needWarning() {
         Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
         Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
 
-        assertEquals(false, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isFalse();
 
         verify(mMockContext, times(1)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(0)).setPowerSaveMode(anyBoolean());
@@ -83,11 +81,11 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_enable_secondCall_needWarning() throws Exception {
+    public void testSetPowerSaveMode_enable_secondCall_needWarning() {
         Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
         Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
 
-        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
 
         verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -97,11 +95,11 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_enable_thridCall_needWarning() throws Exception {
+    public void testSetPowerSaveMode_enable_thridCall_needWarning() {
         Secure.putInt(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1); // Already acked.
         Secure.putInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 1);
 
-        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, true));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, true)).isTrue();
 
         verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -111,11 +109,11 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_enable_firstCall_noWarning() throws Exception {
+    public void testSetPowerSaveMode_enable_firstCall_noWarning() {
         Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
         Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
 
-        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, true, false));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, true, false)).isTrue();
 
         verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(true));
@@ -125,12 +123,12 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_disable_firstCall_noWarning() throws Exception {
+    public void testSetPowerSaveMode_disable_firstCall_noWarning() {
         Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
         Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
 
         // When disabling, needFirstTimeWarning doesn't matter.
-        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, false));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, false)).isTrue();
 
         verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -141,12 +139,12 @@
     }
 
     @Test
-    public void testSetPowerSaveMode_disable_firstCall_needWarning() throws Exception {
+    public void testSetPowerSaveMode_disable_firstCall_needWarning() {
         Secure.putString(mMockResolver, Secure.LOW_POWER_WARNING_ACKNOWLEDGED, "null");
         Secure.putString(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, "null");
 
         // When disabling, needFirstTimeWarning doesn't matter.
-        assertEquals(true, BatterySaverUtils.setPowerSaveMode(mMockContext, false, true));
+        assertThat(BatterySaverUtils.setPowerSaveMode(mMockContext, false, true)).isTrue();
 
         verify(mMockContext, times(0)).sendBroadcast(any(Intent.class));
         verify(mMockPowerManager, times(1)).setPowerSaveMode(eq(false));
@@ -157,7 +155,7 @@
     }
 
     @Test
-    public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
+    public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() {
         Global.putInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
 
         BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
@@ -172,7 +170,7 @@
     }
 
     @Test
-    public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception {
+    public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() {
         Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
         Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
index 9b1fe5f..bbf807d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
@@ -31,7 +31,6 @@
 import android.content.pm.PackageManager;
 import android.os.IDeviceIdleController;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowDefaultDialerManager;
 import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
 
@@ -40,12 +39,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowPackageManager;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
 public class PowerWhitelistBackendTest {
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
index 49dde0e..35743c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BatteryMeterDrawableBaseTest.java
@@ -16,8 +16,8 @@
 
 package com.android.settingslib.graph;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
@@ -25,17 +25,16 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.util.ReflectionHelpers;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BatteryMeterDrawableBaseTest {
     private static final int CRITICAL_LEVEL = 5;
     private static final int PADDING = 5;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
index 5dbb5ca..1b350cc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/graph/BluetoothDeviceLayerDrawableTest.java
@@ -22,14 +22,14 @@
 import android.graphics.drawable.VectorDrawable;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class BluetoothDeviceLayerDrawableTest {
     private static final int RES_ID = R.drawable.ic_bt_cellphone;
     private static final int BATTERY_LEVEL = 15;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index fa64afe..b930aa6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -25,26 +25,21 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilCompatTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
 
     private static HashSet<String> asHashSet(String... strings) {
-        HashSet<String> hashSet = new HashSet<>();
-        for (String s : strings) {
-            hashSet.add(s);
-        }
-        return hashSet;
+        return new HashSet<>(Arrays.asList(strings));
     }
 
     @Test
@@ -105,7 +100,6 @@
                 "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
                 .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
                         "ime1", asHashSet("subtype1", "subtype2"));
-
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 03ab261..84606b4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -25,26 +25,21 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class InputMethodAndSubtypeUtilTest {
 
     private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
 
     private static HashSet<String> asHashSet(String... strings) {
-        HashSet<String> hashSet = new HashSet<>();
-        for (String s : strings) {
-            hashSet.add(s);
-        }
-        return hashSet;
+        return new HashSet<>(Arrays.asList(strings));
     }
 
     @Test
@@ -103,7 +98,6 @@
                 "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
                 .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
                         "ime1", asHashSet("subtype1", "subtype2"));
-
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index b00476b2..4b5e909 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -18,10 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.ByteArrayInputStream;
@@ -32,7 +31,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LicenseHtmlGeneratorFromXmlTest {
     private static final String VALILD_XML_STRING =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -92,8 +91,8 @@
 
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
-        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
                 new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
@@ -107,8 +106,8 @@
 
     @Test(expected = XmlPullParserException.class)
     public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
-        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
                 new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
@@ -117,8 +116,8 @@
 
     @Test
     public void testGenerateHtml() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
-        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         fileNameToContentIdMap.put("/file0", "0");
         fileNameToContentIdMap.put("/file1", "0");
@@ -132,8 +131,8 @@
 
     @Test
     public void testGenerateHtmlWithCustomHeading() {
-        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
-        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+        Map<String, String> fileNameToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         fileNameToContentIdMap.put("/file0", "0");
         fileNameToContentIdMap.put("/file1", "0");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index c90de5f..e82bc06 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -20,14 +20,13 @@
 
 import android.content.Context;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
@@ -37,7 +36,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = LicenseHtmlLoaderCompatTest.ShadowLicenseHtmlLoaderCompat.class)
 public class LicenseHtmlLoaderCompatTest {
 
@@ -58,7 +57,7 @@
 
     @Test
     public void testLoadInBackground() {
-        ArrayList<File> xmlFiles = new ArrayList();
+        ArrayList<File> xmlFiles = new ArrayList<>();
         xmlFiles.add(new File("test.xml"));
         File cachedHtmlFile = new File("test.html");
 
@@ -69,7 +68,7 @@
 
     @Test
     public void testLoadInBackgroundWithNoVaildXmlFiles() {
-        ArrayList<File> xmlFiles = new ArrayList();
+        ArrayList<File> xmlFiles = new ArrayList<>();
         File cachedHtmlFile = new File("test.html");
 
         setupFakeData(xmlFiles, cachedHtmlFile, true, true);
@@ -79,7 +78,7 @@
 
     @Test
     public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
-        ArrayList<File> xmlFiles = new ArrayList();
+        ArrayList<File> xmlFiles = new ArrayList<>();
         xmlFiles.add(new File("test.xml"));
         File cachedHtmlFile = new File("test.html");
 
@@ -90,7 +89,7 @@
 
     @Test
     public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
-        ArrayList<File> xmlFiles = new ArrayList();
+        ArrayList<File> xmlFiles = new ArrayList<>();
         xmlFiles.add(new File("test.xml"));
         File cachedHtmlFile = new File("test.html");
 
@@ -112,10 +111,10 @@
     @Implements(LicenseHtmlLoaderCompat.class)
     public static class ShadowLicenseHtmlLoaderCompat {
 
-        public static List<File> sValidXmlFiles;
-        public static File sCachedHtmlFile;
-        public static boolean sIsCachedHtmlFileOutdated;
-        public static boolean sGenerateHtmlFileSucceeded;
+        private static List<File> sValidXmlFiles;
+        private static File sCachedHtmlFile;
+        private static boolean sIsCachedHtmlFileOutdated;
+        private static boolean sGenerateHtmlFileSucceeded;
 
         @Resetter
         public static void reset() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
index c29481f..8c2e899 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/InjectedSettingTest.java
@@ -18,12 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public final class InjectedSettingTest {
 
     private static final String TEST_STRING = "test";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
index 9c168f7..08d5367 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAppsTest.java
@@ -2,7 +2,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.isA;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -17,20 +17,19 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class RecentLocationAppsTest {
 
     private static final int TEST_UID = 1234;
@@ -56,8 +55,6 @@
     private int mTestUserId;
     private RecentLocationApps mRecentLocationApps;
 
-
-
     @Before
     public void setUp() throws NameNotFoundException {
         MockitoAnnotations.initMocks(this);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 50044f2..acf99a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
@@ -35,23 +34,19 @@
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsSession;
 import android.net.NetworkStatsHistory;
-import android.net.NetworkStatsHistory.Entry;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
-import android.util.FeatureFlagUtils;
-
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class DataUsageControllerTest {
 
     private static final String SUB_ID = "Test Subscriber";
@@ -62,16 +57,18 @@
     private TelephonyManager mTelephonyManager;
     @Mock
     private NetworkStatsManager mNetworkStatsManager;
-
+    @Mock
     private Context mContext;
+
     private DataUsageController mController;
     private NetworkStatsHistory mNetworkStatsHistory;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
-        mController = spy(new DataUsageController(mContext));
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+        mController = new DataUsageController(mContext);
         mNetworkStatsHistory = spy(
                 new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
         doReturn(mNetworkStatsHistory)
@@ -80,78 +77,25 @@
     }
 
     @Test
-    public void getHistoricalUsageLevel_v1_noNetworkSession_shouldReturnNegative1() {
-        FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
-        doReturn(null).when(mController).getSession();
+    public void getHistoricalUsageLevel_shouldQuerySummaryForDevice() throws Exception {
 
-        assertThat(mController.getHistoricalUsageLevel(null /* template */)).isEqualTo(-1L);
-
-    }
-
-    @Test
-    public void getHistoriclUsageLevel_v1_noUsageData_shouldReturn0() {
-        FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
-        doReturn(mSession).when(mController).getSession();
-
-        assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
-                .isEqualTo(0L);
-
-    }
-
-    @Test
-    public void getHistoricalUsageLevel_v1_hasUsageData_shouldReturnTotalUsage() {
-        FeatureFlagUtils.setEnabled(mContext, DataUsageController.DATA_USAGE_V2, false);
-        doReturn(mSession).when(mController).getSession();
-        final long receivedBytes = 743823454L;
-        final long transmittedBytes = 16574289L;
-        final Entry entry = new Entry();
-        entry.bucketStart = 1521583200000L;
-        entry.rxBytes = receivedBytes;
-        entry.txBytes = transmittedBytes;
-        when(mNetworkStatsHistory.getValues(eq(0L), anyLong(), anyLong(), nullable(Entry.class)))
-                .thenReturn(entry);
-
-        assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
-                .isEqualTo(receivedBytes + transmittedBytes);
-
-    }
-
-    @Test
-    public void getHistoricalUsageLevel_v2_shouldQuerySummaryForDevice() throws Exception {
-        final Context context = mock(Context.class);
-        FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
-        when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
-        when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
-        final DataUsageController controller = new DataUsageController(context);
-
-        controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
+        mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard());
 
         verify(mNetworkStatsManager).querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
                 eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */);
     }
 
     @Test
-    public void getHistoricalUsageLevel_v2NoUsageData_shouldReturn0() throws Exception {
-        final Context context = mock(Context.class);
-        FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
-        when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
-        when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+    public void getHistoricalUsageLevel_noUsageData_shouldReturn0() throws Exception {
         when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
                 eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */))
                 .thenReturn(mock(NetworkStats.Bucket.class));
-        final DataUsageController controller = new DataUsageController(context);
-
-        assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+        assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
             .isEqualTo(0L);
     }
 
     @Test
-    public void getHistoricalUsageLevel_v2HasUsageData_shouldReturnTotalUsage()
-            throws Exception {
-        final Context context = mock(Context.class);
-        FeatureFlagUtils.setEnabled(context, DataUsageController.DATA_USAGE_V2, true);
-        when(context.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
-        when(context.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
+    public void getHistoricalUsageLevel_hasUsageData_shouldReturnTotalUsage() throws Exception {
         final long receivedBytes = 743823454L;
         final long transmittedBytes = 16574289L;
         final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
@@ -159,9 +103,8 @@
         when(bucket.getTxBytes()).thenReturn(transmittedBytes);
         when(mNetworkStatsManager.querySummaryForDevice(eq(ConnectivityManager.TYPE_WIFI),
                 eq(SUB_ID), eq(0L) /* startTime */, anyLong() /* endTime */)).thenReturn(bucket);
-        final DataUsageController controller = new DataUsageController(context);
 
-        assertThat(controller.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
+        assertThat(mController.getHistoricalUsageLevel(NetworkTemplate.buildTemplateWifiWildcard()))
                 .isEqualTo(receivedBytes + transmittedBytes);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 0a03631..011f234 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -27,15 +27,14 @@
 import android.os.RemoteException;
 import android.text.format.DateUtils;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class NetworkCycleChartDataLoaderTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 2314f27..d915963 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -19,7 +19,7 @@
 import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -33,15 +33,14 @@
 import android.net.NetworkPolicyManager;
 import android.text.format.DateUtils;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class NetworkCycleDataForUidLoaderTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 9d60a97..2d8ea12 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,8 +16,8 @@
 
 package com.android.settingslib.net;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.nullable;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -37,20 +37,19 @@
 import android.text.format.DateUtils;
 import android.util.Range;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.util.ReflectionHelpers;
 
 import java.time.ZonedDateTime;
 import java.util.Iterator;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class NetworkCycleDataLoaderTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
index 89c319a..59d5674 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -16,11 +16,10 @@
 
 package com.android.settingslib.notification;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -38,16 +37,15 @@
 import android.service.notification.Condition;
 import android.view.LayoutInflater;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class EnableZenModeDialogTest {
     private EnableZenModeDialog mController;
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
index 8147656..437c0d4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java
@@ -25,27 +25,23 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.provider.Settings;
-import android.service.notification.Condition;
 import android.view.LayoutInflater;
 import android.view.View;
 
 import androidx.appcompat.app.AlertDialog;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class ZenDurationDialogTest {
     private ZenDurationDialog mController;
 
     private Context mContext;
     private LayoutInflater mLayoutInflater;
-    private Condition mCountdownCondition;
-    private Condition mAlarmCondition;
     private ContentResolver mContentResolver;
     private AlertDialog.Builder mBuilder;
 
@@ -102,7 +98,6 @@
                 ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked());
     }
 
-
     @Test
     public void testChooseAlwaysPromptSetting() {
         Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
index 449451a..ffaa7443 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinCompatTest.java
@@ -31,7 +31,6 @@
 import androidx.lifecycle.LifecycleOwner;
 import androidx.loader.app.LoaderManager;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.After;
@@ -40,10 +39,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowSuggestionController.class)
 public class SuggestionControllerMixinCompatTest {
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
index aac582f..4dc80f4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionControllerMixinTest.java
@@ -31,7 +31,6 @@
 
 import androidx.lifecycle.LifecycleOwner;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.After;
@@ -40,10 +39,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = ShadowSuggestionController.class)
 public class SuggestionControllerMixinTest {
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
index f4afdb1..3e91641 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowDefaultDialerManager.java
@@ -26,19 +26,19 @@
 @Implements(DefaultDialerManager.class)
 public class ShadowDefaultDialerManager {
 
-    private static String sDefaultDailer;
+    private static String sDefaultDialer;
 
     @Resetter
     public void reset() {
-        sDefaultDailer = null;
+        sDefaultDialer = null;
     }
 
     @Implementation
     public static String getDefaultDialerApplication(Context context) {
-        return sDefaultDailer;
+        return sDefaultDialer;
     }
 
     public static void setDefaultDialerApplication(String dialer) {
-        sDefaultDailer = dialer;
+        sDefaultDialer = dialer;
     }
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index 4705cd2..9a169d2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -27,7 +27,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.testutils.shadow.ShadowActivityManager;
 
 import org.junit.After;
@@ -36,6 +35,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
@@ -45,7 +45,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(shadows = { ShadowActivityManager.class, UserManagerHelperRoboTest.ShadowUserHandle.class})
 public class UserManagerHelperRoboTest {
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
index 645dfa1..026ad47 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/IconCacheTest.java
@@ -30,14 +30,13 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class IconCacheTest {
     private Icon mIcon;
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 6a9579b..7ef31df 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -22,31 +22,30 @@
 
 import android.content.Context;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.time.Duration;
 import java.util.regex.Pattern;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class PowerUtilTest {
-    public static final String TEST_BATTERY_LEVEL_10 = "10%";
-    public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
-    public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
-    public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
-    public static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
-    public static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
-    public static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
-    public static final String ENHANCED_SUFFIX = " based on your usage";
+    private static final String TEST_BATTERY_LEVEL_10 = "10%";
+    private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+    private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+    private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+    private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+    private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
+    private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
+    private static final String ENHANCED_SUFFIX = " based on your usage";
     // matches a time (ex: '1:15 PM', '2 AM', '23:00')
-    public static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
+    private static final String TIME_OF_DAY_REGEX = " (\\d)+:?(\\d)* ((AM)*)|((PM)*)";
     // matches a percentage with parenthesis (ex: '(10%)')
-    public static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
+    private static final String PERCENTAGE_REGEX = " \\(\\d?\\d%\\)";
 
     private Context mContext;
 
@@ -108,7 +107,6 @@
                         + "(" + PERCENTAGE_REGEX + "){0}")); // no percentage
     }
 
-
     @Test
     public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
         String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index e4bbbcb..8fbbfbb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -25,14 +25,13 @@
 import android.text.format.DateUtils;
 import android.text.style.TtsSpan;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class StringUtilTest {
     private Context mContext;
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
index 1e066b1..26db124 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/ThreadUtilsTest.java
@@ -15,28 +15,22 @@
  */
 package com.android.settingslib.utils;
 
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.fail;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowLooper;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class ThreadUtilsTest {
 
     @Test
     public void testMainThread() throws InterruptedException {
         assertThat(ThreadUtils.isMainThread()).isTrue();
-        Thread background = new Thread(new Runnable() {
-            public void run() {
-                assertThat(ThreadUtils.isMainThread()).isFalse();
-            }
-        });
+        Thread background = new Thread(() -> assertThat(ThreadUtils.isMainThread()).isFalse());
         background.start();
         background.join();
     }
@@ -44,13 +38,11 @@
     @Test
     public void testEnsureMainThread() throws InterruptedException {
         ThreadUtils.ensureMainThread();
-        Thread background = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    ThreadUtils.ensureMainThread();
-                    fail("Should not pass ensureMainThread in a background thread");
-                } catch (RuntimeException e) {
-                }
+        Thread background = new Thread(() -> {
+            try {
+                ThreadUtils.ensureMainThread();
+                fail("Should not pass ensureMainThread in a background thread");
+            } catch (RuntimeException expected) {
             }
         });
         background.start();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
new file mode 100644
index 0000000..88fef08
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/ActionButtonsPreferenceTest.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2018 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.preference.PreferenceViewHolder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ActionButtonsPreferenceTest {
+
+    private Context mContext;
+    private View mRootView;
+    private ActionButtonsPreference mPref;
+    private PreferenceViewHolder mHolder;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+        mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
+        mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
+        mPref = new ActionButtonsPreference(mContext);
+    }
+
+    @Test
+    public void onBindViewHolder_setTitle_shouldShowButtonByDefault() {
+        mPref.setButton1Text(R.string.install_other_apps);
+        mPref.setButton2Text(R.string.install_other_apps);
+        mPref.setButton3Text(R.string.install_other_apps);
+        mPref.setButton4Text(R.string.install_other_apps);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onBindViewHolder_setIcon_shouldShowButtonByDefault() {
+        mPref.setButton1Icon(R.drawable.ic_plus);
+        mPref.setButton2Icon(R.drawable.ic_plus);
+        mPref.setButton3Icon(R.drawable.ic_plus);
+        mPref.setButton4Icon(R.drawable.ic_plus);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onBindViewHolder_notSetTitleOrIcon_shouldNotShowButtonByDefault() {
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onBindViewHolder_setVisibleIsGoneAndSetTitle_shouldNotShowButton() {
+        mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+        mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+        mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+        mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onBindViewHolder_setVisibleIsGoneAndSetIcon_shouldNotShowButton() {
+        mPref.setButton1Icon(R.drawable.ic_plus).setButton1Visible(false);
+        mPref.setButton2Icon(R.drawable.ic_plus).setButton2Visible(false);
+        mPref.setButton3Icon(R.drawable.ic_plus).setButton3Visible(false);
+        mPref.setButton4Icon(R.drawable.ic_plus).setButton4Visible(false);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onBindViewHolder_setVisibility_shouldUpdateButtonVisibility() {
+        mPref.setButton1Text(R.string.install_other_apps).setButton1Visible(false);
+        mPref.setButton2Text(R.string.install_other_apps).setButton2Visible(false);
+        mPref.setButton3Text(R.string.install_other_apps).setButton3Visible(false);
+        mPref.setButton4Text(R.string.install_other_apps).setButton4Visible(false);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.GONE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.GONE);
+
+        mPref.setButton1Visible(true);
+        mPref.setButton2Visible(true);
+        mPref.setButton3Visible(true);
+        mPref.setButton4Visible(true);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button2).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button3).getVisibility())
+                .isEqualTo(View.VISIBLE);
+        assertThat(mRootView.findViewById(R.id.button4).getVisibility())
+                .isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onBindViewHolder_setEnabled_shouldEnableButton() {
+        mPref.setButton1Enabled(true);
+        mPref.setButton2Enabled(false);
+        mPref.setButton3Enabled(true);
+        mPref.setButton4Enabled(false);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(mRootView.findViewById(R.id.button1).isEnabled()).isTrue();
+        assertThat(mRootView.findViewById(R.id.button2).isEnabled()).isFalse();
+        assertThat(mRootView.findViewById(R.id.button3).isEnabled()).isTrue();
+        assertThat(mRootView.findViewById(R.id.button4).isEnabled()).isFalse();
+    }
+
+    @Test
+    public void onBindViewHolder_setText_shouldShowSameText() {
+        mPref.setButton1Text(R.string.install_other_apps);
+        mPref.setButton2Text(R.string.install_other_apps);
+        mPref.setButton3Text(R.string.install_other_apps);
+        mPref.setButton4Text(R.string.install_other_apps);
+
+        mPref.onBindViewHolder(mHolder);
+
+        assertThat(((Button) mRootView.findViewById(R.id.button1)).getText())
+                .isEqualTo(mContext.getText(R.string.install_other_apps));
+        assertThat(((Button) mRootView.findViewById(R.id.button2)).getText())
+                .isEqualTo(mContext.getText(R.string.install_other_apps));
+        assertThat(((Button) mRootView.findViewById(R.id.button3)).getText())
+                .isEqualTo(mContext.getText(R.string.install_other_apps));
+        assertThat(((Button) mRootView.findViewById(R.id.button4)).getText())
+                .isEqualTo(mContext.getText(R.string.install_other_apps));
+    }
+
+    @Test
+    public void onBindViewHolder_setButtonIcon_iconMustDisplayAboveText() {
+        mPref.setButton1Text(R.string.install_other_apps);
+        mPref.setButton1Icon(R.drawable.ic_plus);
+
+        mPref.onBindViewHolder(mHolder);
+        final Drawable[] drawablesAroundText =
+                ((Button) mRootView.findViewById(R.id.button1))
+                        .getCompoundDrawables();
+
+        assertThat(drawablesAroundText[1 /* top */]).isNotNull();
+    }
+
+    @Test
+    public void setButtonIcon_iconResourceIdIsZero_shouldNotDisplayIcon() {
+        mPref.setButton1Text(R.string.install_other_apps);
+        mPref.setButton1Icon(0);
+
+        mPref.onBindViewHolder(mHolder);
+        final Drawable[] drawablesAroundText =
+                ((Button) mRootView.findViewById(R.id.button1))
+                        .getCompoundDrawables();
+
+        assertThat(drawablesAroundText[1 /* top */]).isNull();
+    }
+
+    @Test
+    public void setButtonIcon_iconResourceIdNotExisting_shouldNotDisplayIconAndCrash() {
+        mPref.setButton1Text(R.string.install_other_apps);
+        mPref.setButton1Icon(999999999 /* not existing id */);
+        // Should not crash here
+        mPref.onBindViewHolder(mHolder);
+        final Drawable[] drawablesAroundText =
+                ((Button) mRootView.findViewById(R.id.button1))
+                        .getCompoundDrawables();
+
+        assertThat(drawablesAroundText[1 /* top */]).isNull();
+    }
+
+    public static ActionButtonsPreference createMock() {
+        final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+        when(pref.setButton1Text(anyInt())).thenReturn(pref);
+        when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+        when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+        when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+        when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+        when(pref.setButton2Text(anyInt())).thenReturn(pref);
+        when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+        when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+        when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+        when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+        when(pref.setButton3Text(anyInt())).thenReturn(pref);
+        when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+        when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+        when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+        when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+        when(pref.setButton4Text(anyInt())).thenReturn(pref);
+        when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+        when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+        when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+        when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+        return pref;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
index a00f12d..d41d511 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -22,14 +22,13 @@
 import android.graphics.drawable.AnimatedRotateDrawable;
 import android.view.View;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class AnimatedImageViewTest {
     private AnimatedImageView mAnimatedImageView;
 
@@ -47,5 +46,4 @@
         AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
         assertThat(drawable.isRunning()).isTrue();
     }
-
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
index e030005..601da051 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinCompatTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -37,9 +36,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class FooterPreferenceMixinCompatTest {
 
     @Mock
@@ -97,5 +97,4 @@
         verify(mScreen).removePreference(any(FooterPreference.class));
         verify(mScreen, times(2)).addPreference(any(FooterPreference.class));
     }
-
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
index 8817ff7..7ae5d2d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceMixinTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -29,7 +29,6 @@
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 
 import org.junit.Before;
@@ -37,10 +36,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowApplication;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class FooterPreferenceMixinTest {
 
     @Mock
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index e0eceb4..0d2399e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -26,14 +26,14 @@
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class FooterPreferenceTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
index 427a611..99261a3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/LayoutPreferenceTest.java
@@ -27,14 +27,13 @@
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LayoutPreferenceTest {
 
     private LayoutPreference mPreference;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
index 10c9dfb..da97cc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/apppreference/AppPreferenceTest.java
@@ -23,14 +23,13 @@
 
 import androidx.preference.PreferenceViewHolder;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class AppPreferenceTest {
 
     private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index 86443bd..c5cbea7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -25,16 +25,15 @@
 import android.content.Context;
 import android.graphics.drawable.ColorDrawable;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class AccessPointPreferenceTest {
 
     private Context mContext = RuntimeEnvironment.application;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
index f0e8c66..b059df1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/TimestampedScoredNetworkTest.java
@@ -22,15 +22,14 @@
 import android.net.WifiKey;
 import android.os.Parcel;
 
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
 import java.util.Date;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class TimestampedScoredNetworkTest {
   private TimestampedScoredNetwork impl;
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 07c50fd..89960cb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -37,19 +37,19 @@
 import android.util.ArraySet;
 
 import com.android.settingslib.R;
-import com.android.settingslib.SettingsLibRobolectricTestRunner;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Set;
 
-@RunWith(SettingsLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class WifiUtilsTest {
     private static final String TEST_SSID = "\"test_ssid\"";
     private static final String TEST_BSSID = "00:00:00:00:00:00";
@@ -79,7 +79,7 @@
         Bundle bundle = new Bundle();
         ArrayList<ScanResult> scanResults = buildScanResultCache();
         bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
-                                  scanResults.toArray(new Parcelable[scanResults.size()]));
+                                  scanResults.toArray(new Parcelable[0]));
         AccessPoint ap = new AccessPoint(mContext, bundle);
 
         when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index cbb6e82..d1824d7 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -855,6 +855,10 @@
                 GlobalSettingsProto.MultiSim.SMS_PROMPT);
         p.end(multiSimToken);
 
+        dumpSetting(s, p,
+                Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
+                GlobalSettingsProto.NATIVE_FLAGS_HEALTH_CHECK_ENABLED);
+
         final long netstatsToken = p.start(GlobalSettingsProto.NETSTATS);
         dumpSetting(s, p,
                 Settings.Global.NETSTATS_ENABLED,
@@ -1302,6 +1306,9 @@
         dumpSetting(s, p,
                 Settings.Global.WARNING_TEMPERATURE,
                 GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL);
+        dumpSetting(s, p,
+                Settings.Global.USB_ALARM_TEMPERATURE,
+                GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL);
         p.end(tempWarningToken);
 
         final long tetherToken = p.start(GlobalSettingsProto.TETHER);
@@ -1910,6 +1917,15 @@
                 SecureSettingsProto.Location.CHANGER);
         p.end(locationToken);
 
+        final long locationAccessCheckToken = p.start(SecureSettingsProto.LOCATION_ACCESS_CHECK);
+        dumpSetting(s, p,
+                Settings.Secure.LOCATION_ACCESS_CHECK_INTERVAL_MILLIS,
+                SecureSettingsProto.LocationAccessCheck.INTERVAL_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.LOCATION_ACCESS_CHECK_DELAY_MILLIS,
+                SecureSettingsProto.LocationAccessCheck.DELAY_MILLIS);
+        p.end(locationAccessCheckToken);
+
         final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN);
         // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
         // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cb55231..83e8369 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -130,6 +130,7 @@
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
+    <uses-permission android:name="android.permission.MANAGE_SMART_SUGGESTIONS" />
     <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.SET_TIME" />
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index f8b61cd..44354bc1 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -302,11 +302,13 @@
     }
 
     /**
-     * Returns whether there is a soft nav bar.
+     * Returns whether there is a soft nav bar on specified display.
+     *
+     * @param displayId the id of display to check if there is a software navigation bar.
      */
-    public boolean hasSoftNavigationBar() {
+    public boolean hasSoftNavigationBar(int displayId) {
         try {
-            return mIwm.hasNavigationBar();
+            return mIwm.hasNavigationBar(displayId);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
index dfa38ba..8723fb9 100644
--- a/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/legacy/recents/src/com/android/systemui/recents/views/RecentsView.java
@@ -631,7 +631,8 @@
                     }
                 };
                 WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
-                        future, animStartedListener, getHandler(), true /* scaleUp */);
+                        future, animStartedListener, getHandler(), true /* scaleUp */,
+                        getContext().getDisplayId());
                 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP,
                         event.task.getTopComponent().flattenToShortString());
             } else {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index f492208..4891e50 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -30,6 +30,12 @@
     int VERSION = 1;
 
     void startPendingIntentDismissingKeyguard(PendingIntent intent);
+
+    /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but
+     * allow you to specify the callback that is executed after the intent is sent.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent, Runnable intentSentCallback);
     void startActivity(Intent intent, boolean dismissShade);
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
deleted file mode 100644
index 3cc98d8..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_1.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="500"
-        android:propertyXName="translateX"
-        android:propertyYName="translateY"
-        android:pathData="M 0,0 c 31.33333,0 156.66667,0 188,0 "
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
deleted file mode 100644
index eda843d..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_2.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="33"
-            android:propertyXName="translateX"
-            android:propertyYName="translateY"
-            android:pathData="M -12,18 L -12,18" />
-        <objectAnimator
-            android:duration="500"
-            android:propertyXName="translateX"
-            android:propertyYName="translateY"
-            android:pathData="M -12,18 c 31.33333,0 156.66667,0 188,0 "
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
deleted file mode 100644
index cab3d5c..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_3.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="67"
-            android:propertyXName="translateX"
-            android:propertyYName="translateY"
-            android:pathData="M -24,36 L -24,36" />
-        <objectAnimator
-            android:duration="500"
-            android:propertyXName="translateX"
-            android:propertyYName="translateY"
-            android:pathData="M -24,36 c 31.33333,0 156.66667,0 188,0 "
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
deleted file mode 100644
index e435d9a..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="150"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="1"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="0"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
deleted file mode 100644
index e31a7db..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_1.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="183"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="1"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="0"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml b/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
deleted file mode 100644
index 2409612..0000000
--- a/packages/SystemUI/res/anim/dismiss_all_shape_animation_rectangle_path_1_2.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="217"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="1"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="100"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1"
-            android:valueTo="0"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/fab_elevation.xml b/packages/SystemUI/res/anim/fab_elevation.xml
deleted file mode 100644
index 2c76a86..0000000
--- a/packages/SystemUI/res/anim/fab_elevation.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="true" android:state_pressed="true">
-        <set>
-            <objectAnimator
-                android:duration="@android:integer/config_shortAnimTime"
-                android:propertyName="translationZ"
-                android:valueTo="@dimen/fab_press_translation_z"
-                android:valueType="floatType" />
-        </set>
-    </item>
-    <item>
-        <set>
-            <objectAnimator
-                android:duration="@android:integer/config_shortAnimTime"
-                android:propertyName="translationZ"
-                android:valueTo="0"
-                android:valueType="floatType" />
-        </set>
-    </item>
-</selector>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
deleted file mode 100644
index 8fdad80..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="616"
-        android:propertyName="rotation"
-        android:valueFrom="-90.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml b/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
deleted file mode 100644
index 57132e1..0000000
--- a/packages/SystemUI/res/anim/ic_landscape_to_rotate_landscape_animation.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="466"
-        android:propertyName="scaleX"
-        android:valueFrom="1.0"
-        android:valueTo="0.909"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
-    <objectAnimator
-        android:duration="466"
-        android:propertyName="scaleY"
-        android:valueFrom="1.0"
-        android:valueTo="0.909"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_landscape_to_rotate_animation_interpolator_0" />
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="400"
-            android:propertyName="rotation"
-            android:valueFrom="0.0"
-            android:valueTo="45.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="0.9"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="0.9"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
deleted file mode 100644
index cdb7890..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="616"
-        android:propertyName="rotation"
-        android:valueFrom="0.0"
-        android:valueTo="-221.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
deleted file mode 100644
index 46100b4..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="400"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
deleted file mode 100644
index 8f6d24d..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="400"
-        android:propertyName="rotation"
-        android:valueFrom="0.0"
-        android:valueTo="-135.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
deleted file mode 100644
index 300ed53..0000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="66"
-            android:propertyName="pathData"
-            android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-            android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="216"
-            android:propertyName="pathData"
-            android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-            android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
deleted file mode 100644
index ad2a5fa..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_0_animation.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="scaleX"
-            android:valueFrom="1.0"
-            android:valueTo="0.9"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="333"
-            android:propertyName="scaleY"
-            android:valueFrom="1.0"
-            android:valueTo="0.9"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
deleted file mode 100644
index c152152..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="616"
-        android:propertyName="rotation"
-        android:valueFrom="0.0"
-        android:valueTo="-180.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
deleted file mode 100644
index b2c1eb8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="200"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="fillAlpha"
-            android:valueFrom="1.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
deleted file mode 100644
index 2a9bbe3..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_landscape_landscape_animation.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleX"
-            android:valueFrom="0.909"
-            android:valueTo="0.909"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="166"
-            android:propertyName="scaleX"
-            android:valueFrom="0.909"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
-    </set>
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="116"
-            android:propertyName="scaleY"
-            android:valueFrom="0.909"
-            android:valueTo="0.909"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="166"
-            android:propertyName="scaleY"
-            android:valueFrom="0.909"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@interpolator/ic_rotate_to_landscape_animation_interpolator_0" />
-    </set>
-    <objectAnimator
-        android:duration="616"
-        android:propertyName="rotation"
-        android:valueFrom="45.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
deleted file mode 100644
index ce26770..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="466"
-        android:propertyName="scaleX"
-        android:valueFrom="0.9"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
-    <objectAnimator
-        android:duration="466"
-        android:propertyName="scaleY"
-        android:valueFrom="0.9"
-        android:valueTo="1.0"
-        android:valueType="floatType"
-        android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
deleted file mode 100644
index 6e8941d..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <objectAnimator
-        android:duration="616"
-        android:propertyName="rotation"
-        android:valueFrom="-221.0"
-        android:valueTo="0.0"
-        android:valueType="floatType"
-        android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
deleted file mode 100644
index 3c3c131..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="83"
-            android:propertyName="fillAlpha"
-            android:valueFrom="0.0"
-            android:valueTo="1.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
deleted file mode 100644
index fd8e4f8..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="rotation"
-            android:valueFrom="-135.0"
-            android:valueTo="-135.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="400"
-            android:propertyName="rotation"
-            android:valueFrom="-135.0"
-            android:valueTo="0.0"
-            android:valueType="floatType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
deleted file mode 100644
index a77a536..0000000
--- a/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<set
-    xmlns:android="http://schemas.android.com/apk/res/android" >
-    <set
-        android:ordering="sequentially" >
-        <objectAnimator
-            android:duration="50"
-            android:propertyName="pathData"
-            android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
-            android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/linear" />
-        <objectAnimator
-            android:duration="500"
-            android:propertyName="pathData"
-            android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
-            android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
-            android:valueType="pathType"
-            android:interpolator="@android:interpolator/fast_out_slow_in" />
-    </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_1.xml b/packages/SystemUI/res/anim/ic_signal_blink_1.xml
deleted file mode 100644
index 64580d1..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_1.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-    Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:anim/linear_interpolator"
-    android:duration="@integer/carrier_network_change_anim_time"
-    android:repeatCount="-1">
-
-    <propertyValuesHolder
-        android:propertyName="fillColor"
-        android:valueType="colorType">
-        <keyframe
-            android:fraction="0.0"
-            android:value="?attr/fillColor"/>
-        <keyframe
-            android:fraction="0.32"
-            android:value="?attr/fillColor"/>
-        <keyframe
-            android:fraction="0.33"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="1.0"
-            android:value="?attr/backgroundColor"/>
-    </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_2.xml b/packages/SystemUI/res/anim/ic_signal_blink_2.xml
deleted file mode 100644
index f055cd07..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_2.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
-    Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:anim/linear_interpolator"
-    android:duration="@integer/carrier_network_change_anim_time"
-    android:repeatCount="-1">
-
-    <propertyValuesHolder
-        android:propertyName="fillColor"
-        android:valueType="colorType">
-        <keyframe
-            android:fraction="0.0"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="0.32"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="0.33"
-            android:value="?attr/fillColor"/>
-        <keyframe
-            android:fraction="0.66"
-            android:value="?attr/fillColor"/>
-        <keyframe
-            android:fraction="0.67"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="1.0"
-            android:value="?attr/backgroundColor"/>
-    </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/ic_signal_blink_3.xml b/packages/SystemUI/res/anim/ic_signal_blink_3.xml
deleted file mode 100644
index abcd774..0000000
--- a/packages/SystemUI/res/anim/ic_signal_blink_3.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<!--
-    Copyright (C) 2015 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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:interpolator="@android:anim/linear_interpolator"
-    android:duration="@integer/carrier_network_change_anim_time"
-    android:repeatCount="-1">
-
-    <propertyValuesHolder
-        android:propertyName="fillColor"
-        android:valueType="colorType">
-        <keyframe
-            android:fraction="0.0"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="0.66"
-            android:value="?attr/backgroundColor"/>
-        <keyframe
-            android:fraction="0.67"
-            android:value="?attr/fillColor"/>
-        <keyframe
-            android:fraction="1.0"
-            android:value="?attr/fillColor"/>
-    </propertyValuesHolder>
-
-</objectAnimator>
diff --git a/packages/SystemUI/res/anim/system_out.xml b/packages/SystemUI/res/anim/system_out.xml
deleted file mode 100644
index 4717e47..0000000
--- a/packages/SystemUI/res/anim/system_out.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-    >
-    <alpha android:toAlpha="0.0" android:fromAlpha="1.0"
-        android:duration="@android:integer/config_longAnimTime" 
-        />
-</set>
diff --git a/packages/SystemUI/res/drawable/car_ic_music.xml b/packages/SystemUI/res/drawable/car_ic_music.xml
deleted file mode 100644
index f90cd69..0000000
--- a/packages/SystemUI/res/drawable/car_ic_music.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-    Copyright (C) 2016 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-  <path
-      android:fillAlpha=".1"
-      android:strokeAlpha=".1"
-      android:pathData="M0 0h48v48H0z" />
-  <path
-      android:fillColor="@color/car_grey_50"
-      android:pathData="M24 2C14.06 2 6 10.06 6 20v14c0 3.31 2.69 6 6 6h6V24h-8v-4c0-7.73 6.27-14
-14-14s14 6.27 14 14v4h-8v16h6c3.31 0 6-2.69 6-6V20c0-9.94-8.06-18-18-18z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_ic_navigation.xml b/packages/SystemUI/res/drawable/car_ic_navigation.xml
deleted file mode 100644
index 328efa0..0000000
--- a/packages/SystemUI/res/drawable/car_ic_navigation.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="32dp"
-    android:height="37dp"
-    android:viewportWidth="32.0"
-    android:viewportHeight="37.0">
-  <path
-      android:pathData="M16.62,0.61L31.33,35.21C31.55,35.72 31.31,36.3 30.8,36.52C30.48,36.66 30.12,36.62 29.83,36.42L15.7,26.44L1.58,36.42C1.13,36.73 0.5,36.63 0.18,36.18C-0.02,35.89 -0.06,35.53 0.08,35.21L14.78,0.61C15,0.1 15.59,-0.14 16.1,0.08C16.33,0.18 16.52,0.37 16.62,0.61Z"
-      android:strokeColor="#00000000"
-      android:fillType="evenOdd"
-      android:fillColor="@color/car_grey_50"
-      android:strokeWidth="1"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
deleted file mode 100644
index 61d937b90..0000000
--- a/packages/SystemUI/res/drawable/car_ic_notification.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-    Copyright (C) 2018 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-    <path
-        android:fillColor="#FFFFFF"
-        android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
-4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
-1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
-9v12z" />
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
deleted file mode 100644
index 4651dcb..0000000
--- a/packages/SystemUI/res/drawable/car_ic_overview.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-    Copyright (C) 2016 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="56dp"
-    android:height="56dp"
-    android:viewportWidth="48"
-    android:viewportHeight="48">
-
-    <path
-        android:pathData="M0 0h48v48H0z" />
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
-0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml b/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
deleted file mode 100644
index 25b449a..0000000
--- a/packages/SystemUI/res/drawable/car_rounded_bg_bottom.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackgroundFloating" />
-    <corners
-        android:bottomLeftRadius="@dimen/car_radius_3"
-        android:topLeftRadius="0dp"
-        android:bottomRightRadius="@dimen/car_radius_3"
-        android:topRightRadius="0dp"
-        />
-</shape>
diff --git a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml b/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
deleted file mode 100644
index 9e71cbe..0000000
--- a/packages/SystemUI/res/drawable/dismiss_all_shape_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:drawable="@drawable/dismiss_all_shape" >
-    <target
-        android:name="3"
-        android:animation="@anim/dismiss_all_shape_animation_3" />
-    <target
-        android:name="rectangle_path_1_2"
-        android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_2" />
-    <target
-        android:name="2"
-        android:animation="@anim/dismiss_all_shape_animation_2" />
-    <target
-        android:name="rectangle_path_1_1"
-        android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1_1" />
-    <target
-        android:name="1"
-        android:animation="@anim/dismiss_all_shape_animation_1" />
-    <target
-        android:name="rectangle_path_1"
-        android:animation="@anim/dismiss_all_shape_animation_rectangle_path_1" />
-</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_notification_block.xml b/packages/SystemUI/res/drawable/ic_notification_block.xml
index 572e97b..27690740 100644
--- a/packages/SystemUI/res/drawable/ic_notification_block.xml
+++ b/packages/SystemUI/res/drawable/ic_notification_block.xml
@@ -20,6 +20,6 @@
         android:viewportHeight="24.0">
 
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="#FF000000"
         android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml
index 5ca34b0..1e8cd5a 100644
--- a/packages/SystemUI/res/layout/biometric_dialog.xml
+++ b/packages/SystemUI/res/layout/biometric_dialog.xml
@@ -160,6 +160,15 @@
                         android:maxLines="2"
                         android:text="@string/biometric_dialog_confirm"
                         android:visibility="gone"/>
+                    <!-- Try Again Button -->
+                    <Button android:id="@+id/button_try_again"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        style="@*android:style/Widget.DeviceDefault.Button.Colored"
+                        android:gravity="center"
+                        android:maxLines="2"
+                        android:text="@string/biometric_dialog_try_again"
+                        android:visibility="gone"/>
                     <Space android:id="@+id/rightSpacer"
                         android:layout_width="12dip"
                         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
deleted file mode 100644
index d568d0d..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:id="@+id/nav_buttons"
-        android:gravity="left"
-        android:paddingLeft="30dp"
-        android:layout_weight="1"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="right"
-        android:orientation="horizontal">
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:src="@drawable/car_ic_notification"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="20dp"
-            android:paddingRight="20dp"
-            android:alpha="0.7"
-        />
-
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_clock_end_padding"
-            android:gravity="center_vertical"
-            android:paddingRight="20dp"
-        />
-
-        <Space
-            android:layout_width="10dp"
-            android:layout_height="match_parent"/>
-
-    </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
deleted file mode 100644
index 4ba6c06..0000000
--- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2018, 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:id="@+id/nav_buttons"
-        android:gravity="left"
-        android:paddingLeft="30dp"
-        android:layout_weight="1"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingLeft="30dp"
-            android:paddingRight="30dp"
-        />
-    </LinearLayout>
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
deleted file mode 100644
index 91ba026..0000000
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:orientation="vertical"
-    android:background="@drawable/system_bar_background">
-
-    <LinearLayout
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:id="@+id/nav_buttons"
-        android:orientation="vertical"
-        android:gravity="top"
-        android:paddingTop="30dp"
-        android:layout_weight="1"
-        android:background="@drawable/system_bar_background"
-        android:animateLayoutChanges="true">
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/home"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
-            android:src="@drawable/car_ic_overview"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="30dp"
-            android:paddingBottom="30dp"
-        />
-
-        <com.android.systemui.statusbar.car.CarNavigationButton
-            android:id="@+id/hvac"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
-            systemui:broadcast="true"
-            android:src="@drawable/car_ic_hvac"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="30dp"
-            android:paddingBottom="30dp"
-        />
-
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="bottom"
-        android:orientation="vertical">
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/notifications"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:src="@drawable/car_ic_notification"
-            android:background="?android:attr/selectableItemBackground"
-            android:paddingTop="20dp"
-            android:paddingBottom="20dp"
-            android:alpha="0.7"
-        />
-
-
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_clock_end_padding"
-            android:gravity="center_horizontal"
-            android:paddingBottom="20dp"
-        />
-
-        <Space
-            android:layout_height="10dp"
-            android:layout_width="match_parent"/>
-
-    </LinearLayout>
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
deleted file mode 100644
index f2ef301..0000000
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?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.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.car.CarStatusBarHeader
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/header"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/car_qs_header_system_icons_area_height"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp" >
-
-    <include layout="@layout/system_icons"
-             android:layout_width="wrap_content"
-             android:layout_height="match_parent"
-             android:gravity="center_vertical|end"
-             android:layout_weight="1"
-    />
-
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:singleLine="true"
-        android:paddingStart="@dimen/status_bar_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_clock_end_padding"
-        android:gravity="center_vertical|end"
-    />
-</com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
deleted file mode 100644
index e16014b..0000000
--- a/packages/SystemUI/res/layout/car_top_navigation_bar.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
-** Copyright 2018, 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.
-*/
--->
-
-<com.android.systemui.statusbar.car.CarNavigationBarView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent"
-    android:background="@drawable/system_bar_background">
-
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:singleLine="true"
-        android:paddingStart="@dimen/status_bar_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_clock_end_padding"
-        android:gravity="center_vertical"
-    />
-
-</com.android.systemui.statusbar.car.CarNavigationBarView>
-
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
deleted file mode 100644
index a6beaa1..0000000
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-     Copyright (C) 2018 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.
--->
-<androidx.car.widget.PagedListView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:background="@drawable/car_card_rounded_background"
-    android:id="@+id/volume_list"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_marginStart="@dimen/car_margin"
-    android:layout_marginEnd="@dimen/car_margin"
-    android:minWidth="@dimen/volume_dialog_panel_width"
-    android:theme="@style/Theme.Car.NoActionBar"
-    app:dividerStartMargin="@dimen/car_keyline_1"
-    app:dividerEndMargin="@dimen/car_keyline_1"
-    app:gutter="none"
-    app:showPagedListViewDivider="true"
-    app:scrollBarEnabled="false" />
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index eb3f70a..d502baa 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -144,6 +144,7 @@
 
         <!-- Settings and Done buttons -->
         <LinearLayout
+            android:id="@+id/block_or_minimize"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/notification_guts_button_spacing"
@@ -178,13 +179,6 @@
                 android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
                 style="@style/TextAppearance.NotificationInfo.Button" />
             <TextView
-                android:id="@+id/toggle_silent"
-                android:text="@string/inline_silent_button_silent"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
-                style="@style/TextAppearance.NotificationInfo.Button" />
-            <TextView
                 android:id="@+id/keep"
                 android:minWidth="48dp"
                 android:text="@string/inline_keep_button"
@@ -193,6 +187,44 @@
                 android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing"
                 style="@style/TextAppearance.NotificationInfo.Button"/>
         </LinearLayout>
+        <LinearLayout
+            android:id="@+id/interruptiveness_settings"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/notification_guts_button_spacing"
+            android:layout_marginStart="@dimen/notification_guts_button_side_margin"
+            android:layout_marginEnd="@dimen/notification_guts_button_side_margin"
+            android:gravity="center"
+            android:orientation="horizontal"
+            android:visibility="gone">
+            <TextView
+                android:id="@+id/int_block"
+                android:text="@string/inline_block_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:drawableTop="@drawable/ic_notification_block"
+                android:drawableTint="?android:attr/colorAccent"
+                android:layout_weight="1"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/int_silent"
+                android:text="@string/inline_minimize_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:drawableTop="@drawable/ic_notifications_silence"
+                android:drawableTint="?android:attr/colorAccent"
+                android:layout_weight="1"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+            <TextView
+                android:id="@+id/int_alert"
+                android:text="@string/inline_keep_button"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:drawableTop="@drawable/ic_notifications_alert"
+                android:drawableTint="?android:attr/colorAccent"
+                android:layout_weight="1"
+                style="@style/TextAppearance.NotificationInfo.Button"/>
+        </LinearLayout>
     </LinearLayout>
     <com.android.systemui.statusbar.notification.row.NotificationUndoLayout
         android:id="@+id/confirmation"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
index 2f7d486..bc15f2c4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml
@@ -26,9 +26,7 @@
         android:id="@+id/dialog_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical"
-        android:padding="@dimen/ongoing_appops_dialog_content_padding">
-
+        android:orientation="vertical" >
         <TextView
             android:id="@+id/title"
             android:layout_width="match_parent"
@@ -37,21 +35,29 @@
             android:textDirection="locale"
             android:textAppearance="@style/TextAppearance.AppOpsDialog.Title"
             android:textColor="@*android:color/text_color_primary"
-            android:paddingStart="@dimen/ongoing_appops_dialog_title_padding"
-            android:paddingEnd="@dimen/ongoing_appops_dialog_title_padding"
-            android:paddingBottom="@dimen/ongoing_appops_dialog_sep"
+            android:layout_marginStart="@dimen/ongoing_appops_dialog_title_margin_sides"
+            android:layout_marginEnd="@dimen/ongoing_appops_dialog_title_margin_sides"
+            android:layout_marginBottom="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
+            android:layout_marginTop="@dimen/ongoing_appops_dialog_title_margin_top_bottom"
         />
 
         <LinearLayout
-            android:id="@+id/items_container"
+            android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:gravity="start"
-        />
+            android:layout_marginBottom="@dimen/ongoing_appops_dialog_items_bottom_margin" >
 
-        <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
-                 android:visibility="gone" />
+            <LinearLayout
+                android:id="@+id/items_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="start"
+            />
+
+            <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item"
+                     android:visibility="gone" />
+        </LinearLayout>
 
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
index f05f7ba..ecfbfb4 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_item.xml
@@ -17,37 +17,39 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/ongoing_appops_dialog_line_height"
+    android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
+    android:layout_marginEnd="@dimen/ongoing_appops_dialog_text_padding"
     android:fillViewport="true"
     android:orientation="horizontal"
-    android:layout_marginTop="@dimen/ongoing_appops_dialog_text_margin"
-    android:focusable="true" >
+    android:focusable="true"
+    android:layout_gravity="center_vertical">
 
     <ImageView
         android:id="@+id/app_icon"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
-        android:layout_width="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="@dimen/ongoing_appops_dialog_app_icon_size"
+        android:layout_width="@dimen/ongoing_appops_dialog_app_icon_size"
+        android:layout_gravity="start|center_vertical"
     />
 
     <TextView
         android:id="@+id/app_name"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="match_parent"
         android:layout_width="0dp"
         android:layout_weight="1"
-        android:gravity="bottom|start"
+        android:gravity="start|center_vertical"
         android:textDirection="locale"
         android:textAppearance="@style/TextAppearance.AppOpsDialog.Item"
         android:textColor="@*android:color/text_color_primary"
-        android:paddingStart="@dimen/ongoing_appops_dialog_text_padding"
-        android:paddingEnd="@dimen/ongoing_appops_dialog_text_padding"
-
+        android:layout_marginStart="@dimen/ongoing_appops_dialog_text_padding"
     />
 
     <LinearLayout
         android:id="@+id/icons"
-        android:layout_height="@dimen/ongoing_appops_dialog_icon_height"
+        android:layout_height="match_parent"
         android:layout_width="wrap_content"
         android:gravity="end"
+        android:layout_gravity="end|center_vertical"
         android:visibility="gone"
     />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 0cc3c9e..34c208a 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -56,11 +56,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
-    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
-              android:layout="@layout/car_fullscreen_user_switcher"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"/>
-
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/arrays_car.xml b/packages/SystemUI/res/values/arrays_car.xml
deleted file mode 100644
index 8c760fc..0000000
--- a/packages/SystemUI/res/values/arrays_car.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
-    <!-- These should be overriden in an overlay. The default implementation is empty.
-         There needs to be correspondence per index between these arrays, which means that if there
-         isn't a longpress action associated with a shortcut item, put in an empty item to make
-         sure everything lines up.
-    -->
-    <array name="car_facet_icons" />
-    <array name="car_facet_intent_uris" />
-    <array name="car_facet_longpress_intent_uris" />
-    <array name="car_facet_package_filters"/>
-    <array name="car_facet_category_filters"/>
-</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d8648fa..9e97cd8 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -464,4 +464,6 @@
     <string-array name="config_pluginWhitelist" translatable="false">
         <item>com.android.systemui</item>
     </string-array>
+
+    <integer name="ongoing_appops_dialog_max_apps">5</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0997c5b1..8e0bfb6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -881,6 +881,7 @@
     <dimen name="smart_reply_button_stroke_width">1dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
+    <dimen name="smart_action_button_icon_size">24dp</dimen>
     <dimen name="smart_action_button_icon_padding">10dp</dimen>
 
     <!-- A reasonable upper bound for the height of the smart reply button. The measuring code
@@ -945,14 +946,20 @@
     <dimen name="ongoing_appops_dialog_sep">16dp</dimen>
     <!--Padding around text items in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_text_padding">16dp</dimen>
-    <!-- Height of icons in Ongoing App Ops dialog. Both App Op icon and application icon -->
-    <dimen name="ongoing_appops_dialog_icon_height">28dp</dimen>
-    <!-- Margin between text lines in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_text_margin">15dp</dimen>
-    <!-- Side padding of title in Ongoing App Ops dialog -->
-    <dimen name="ongoing_appops_dialog_title_padding">10dp</dimen>
-    <!-- Padding around Ongoing App Ops dialog content -->
-    <dimen name="ongoing_appops_dialog_content_padding">24dp</dimen>
+    <!-- Height and width of App Opp icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_icon_size">24dp</dimen>
+    <!-- Left margin of App Opp icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_icon_margin">8dp</dimen>
+    <!-- Height and width of Application icons in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_app_icon_size">32dp</dimen>
+    <!-- Height of line in Ongoing App Ops dialog-->
+    <dimen name="ongoing_appops_dialog_line_height">48dp</dimen>
+    <!-- Side margin of title in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_title_margin_sides">24dp</dimen>
+    <!-- Bottom margin of items in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
+    <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
+    <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
     <!-- Side margins around the Ongoing App Ops chip-->
     <dimen name="ongoing_appops_chip_margin">12dp</dimen>
     <!-- Top and bottom margins around the Ongoing App Ops chip -->
@@ -968,9 +975,9 @@
     <!-- Radius of Ongoing App Ops chip corners -->
     <dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
-    <dimen name="ongoing_appops_dialog_title_size">24sp</dimen>
+    <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
-    <dimen name="ongoing_appops_dialog_item_size">20sp</dimen>
+    <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f68656b..9917257 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -272,6 +272,8 @@
     <string name="accessibility_biometric_dialog_help_area">Help message area</string>
     <!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
     <string name="biometric_dialog_confirm">Confirm</string>
+    <!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR_LIMIT=30] -->
+    <string name="biometric_dialog_try_again">Try again</string>
 
     <!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication -->
     <string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string>
@@ -700,6 +702,8 @@
     <string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
     <!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
     <string name="quick_settings_bluetooth_secondary_label_input">Input</string>
+    <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]-->
+    <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string>
     <!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] -->
     <string name="quick_settings_bluetooth_secondary_label_transient">Turning on&#8230;</string>
     <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
@@ -1556,17 +1560,26 @@
     <!-- Notification inline controls: block notifications button -->
     <string name="inline_stop_button">Stop notifications</string>
 
+    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+    <string name="inline_block_button">Block</string>
+
     <!-- Notification inline controls: keep getting notifications button -->
     <string name="inline_keep_button">Keep showing</string>
 
     <!-- Notification inline controls: minimize notifications button -->
     <string name="inline_minimize_button">Minimize</string>
 
-    <!-- Notification inline controls: show notifications silently button [CHAR_LIMIT=25] -->
+    <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
     <string name="inline_silent_button_silent">Show silently</string>
 
-    <!-- Notification inline controls: show and alert button [CHAR_LIMIT=25] -->
-    <string name="inline_silent_button_alert">Show and alert</string>
+    <!-- Notification inline controls: button to continue showing notifications silently [CHAR_LIMIT=35] -->
+    <string name="inline_silent_button_stay_silent">Stay silent</string>
+
+    <!-- Notification inline controls: button to make notifications alert the user [CHAR_LIMIT=35] -->
+    <string name="inline_silent_button_alert">Alert me</string>
+
+    <!-- Notification inline controls: button to continue alerting the user when notifications arrive [CHAR_LIMIT=35] -->
+    <string name="inline_silent_button_keep_alerting">Keep alerting</string>
 
     <!-- Notification Inline controls: continue receiving notifications prompt, app level -->
     <string name="inline_keep_showing_app">Keep showing notifications from this app?</string>
@@ -2255,6 +2268,7 @@
 
     <!-- Text on chip for multiple apps using a single app op [CHAR LIMIT=10] -->
     <plurals name="ongoing_privacy_chip_multiple_apps">
+        <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> app</item>
         <item quantity="few"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
         <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> apps</item>
     </plurals>
@@ -2267,6 +2281,7 @@
 
     <!-- Content description for ongoing privacy chip. Use with multiple apps using same app op[CHAR LIMIT=NONE]-->
     <plurals name="ongoing_privacy_chip_content_multiple_apps_single_op">
+        <item quantity="one"><xliff:g id="num_apps" example="1">%1$d</xliff:g> application is using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
         <item quantity="few"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
         <item quantity="other"><xliff:g id="num_apps" example="3">%1$d</xliff:g> applications are using your <xliff:g id="type" example="camera">%2$s</xliff:g>.</item>
     </plurals>
@@ -2301,6 +2316,6 @@
     <!-- Text for indicating extra apps using app ops [CHAR LIMIT=NONE] -->
     <plurals name="ongoing_privacy_dialog_overflow_text">
         <item quantity="one"><xliff:g id="num_apps" example="1">%d</xliff:g> other app</item>
-        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other app</item>
+        <item quantity="other"><xliff:g id="num_apps" example="3">%d</xliff:g> other apps</item>
     </plurals>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e9aa1b6..fede934 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -262,7 +262,7 @@
 
     <style name="TextAppearance.AppOpsDialog.Item">
         <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">sans-serif</item>
     </style>
 
     <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/res/xml/car_volume_items.xml b/packages/SystemUI/res/xml/car_volume_items.xml
deleted file mode 100644
index 742dfdd..0000000
--- a/packages/SystemUI/res/xml/car_volume_items.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- *
- * Copyright 2018, 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.
-*/
--->
-
-<!--
-  Defines all possible items on car volume settings UI, keyed by usage.
-
-  This enables the CarSettings UI to associate VolumeGroups surfaced by
-  CarAudioManager.getVolumeGroupCount with renderable assets (ie: title, icon)
-  for presentation.
-
-  Order matters in this configuration. If one volume group contains multiple
-  audio usages, the first one appears in this file would be picked to be
-  presented on UI.
-
-  When overriding this configuration, please consult also the
-  car_volume_groups.xml, which is read by car audio service.
--->
-<carVolumeItems xmlns:car="http://schemas.android.com/apk/res-auto">
-  <item car:usage="unknown"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="media"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="voice_communication"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="voice_communication_signalling"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="alarm"
-        car:icon="@*android:drawable/ic_audio_alarm"/>
-  <item car:usage="notification"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_ringtone"
-        car:icon="@*android:drawable/ic_audio_ring_notif"/>
-  <item car:usage="notification_communication_request"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_communication_instant"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_communication_delayed"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="notification_event"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="assistance_accessibility"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="assistance_navigation_guidance"
-        car:icon="@drawable/car_ic_navigation"/>
-  <item car:usage="assistance_sonification"
-        car:icon="@drawable/car_ic_notification"/>
-  <item car:usage="game"
-        car:icon="@drawable/car_ic_music"/>
-  <item car:usage="assistant"
-        car:icon="@drawable/car_ic_music"/>
-</carVolumeItems>
-
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index ece2bb9..f3bdbae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -71,4 +71,9 @@
      */
     void onStatusBarMotionEvent(in MotionEvent event) = 9;
 
+    /**
+     * Get the corner radius of windows in pixels.
+     */
+    float getWindowCornerRadius() = 10;
+
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
index 65c5220..a9cf857 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplier.java
@@ -83,6 +83,7 @@
         t.setWindowCrop(params.surface, params.windowCrop);
         t.setAlpha(params.surface, params.alpha);
         t.setLayer(params.surface, params.layer);
+        t.setCornerRadius(params.surface, params.cornerRadius);
         t.show(params.surface);
     }
 
@@ -98,12 +99,13 @@
          * @param windowCrop Crop to apply.
          */
         public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
-                Rect windowCrop, int layer) {
+                Rect windowCrop, int layer, float cornerRadius) {
             this.surface = surface.mSurfaceControl;
             this.alpha = alpha;
             this.matrix = new Matrix(matrix);
             this.windowCrop = new Rect(windowCrop);
             this.layer = layer;
+            this.cornerRadius = cornerRadius;
         }
 
         final SurfaceControl surface;
@@ -111,5 +113,6 @@
         final Matrix matrix;
         final Rect windowCrop;
         final int layer;
+        final float cornerRadius;
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index 70258c2..2aba3fa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -20,7 +20,6 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
 public class TransactionCompat {
@@ -53,7 +52,7 @@
     }
 
     public TransactionCompat setSize(SurfaceControlCompat surfaceControl, int w, int h) {
-        mTransaction.setSize(surfaceControl.mSurfaceControl, w, h);
+        mTransaction.setBufferSize(surfaceControl.mSurfaceControl, w, h);
         return this;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 42e60aa..8a251ae 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
@@ -61,7 +62,7 @@
     public static final int TRANSIT_KEYGUARD_OCCLUDE = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
     public static final int TRANSIT_KEYGUARD_UNOCCLUDE = WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
-    public static final int NAV_BAR_POS_INVALID = -1;
+    public static final int NAV_BAR_POS_INVALID = NAV_BAR_INVALID;
     public static final int NAV_BAR_POS_LEFT = NAV_BAR_LEFT;
     public static final int NAV_BAR_POS_RIGHT = NAV_BAR_RIGHT;
     public static final int NAV_BAR_POS_BOTTOM = NAV_BAR_BOTTOM;
@@ -100,23 +101,23 @@
      * Overrides a pending app transition.
      */
     public void overridePendingAppTransitionMultiThumbFuture(
-            AppTransitionAnimationSpecsFuture animationSpecFuture,
-            Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) {
+            AppTransitionAnimationSpecsFuture animationSpecFuture, Runnable animStartedCallback,
+            Handler animStartedCallbackHandler, boolean scaleUp, int displayId) {
         try {
             WindowManagerGlobal.getWindowManagerService()
                     .overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(),
                             RecentsTransition.wrapStartedListener(animStartedCallbackHandler,
-                                    animStartedCallback), scaleUp);
+                                    animStartedCallback), scaleUp, displayId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e);
         }
     }
 
     public void overridePendingAppTransitionRemote(
-            RemoteAnimationAdapterCompat remoteAnimationAdapter) {
+            RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) {
         try {
             WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote(
-                    remoteAnimationAdapter.getWrapped());
+                    remoteAnimationAdapter.getWrapped(), displayId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override pending app transition (remote): ", e);
         }
@@ -159,11 +160,13 @@
     }
 
     /**
-     * @return whether there is a soft nav bar.
+     * @param displayId the id of display to check if there is a software navigation bar.
+     *
+     * @return whether there is a soft nav bar on specific display.
      */
-    public boolean hasSoftNavigationBar() {
+    public boolean hasSoftNavigationBar(int displayId) {
         try {
-            return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
+            return WindowManagerGlobal.getWindowManagerService().hasNavigationBar(displayId);
         } catch (RemoteException e) {
             return false;
         }
@@ -176,10 +179,9 @@
      * @see #NAV_BAR_POS_BOTTOM
      * @see #NAV_BAR_POS_INVALID
      */
-    public int getNavBarPosition() {
+    public int getNavBarPosition(int displayId) {
         try {
-            // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
-            return WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+            return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to get nav bar position");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index e1b8dc8..9e7c5ba 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -36,6 +36,15 @@
     }
 
     @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentCallback) {
+        if (mActualStarter == null) {
+            return;
+        }
+        mActualStarter.startPendingIntentDismissingKeyguard(intent, intentSentCallback);
+    }
+
+    @Override
     public void startActivity(Intent intent, boolean dismissShade) {
         if (mActualStarter == null) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 417d516..867c917 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -62,6 +62,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.SmartReplyConstants;
+import com.android.systemui.volume.VolumeDialogComponent;
 
 import java.util.function.Consumer;
 
@@ -132,6 +133,10 @@
         return new QSTileHost(context, statusBar, iconController);
     }
 
+    public VolumeDialogComponent createVolumeDialogComponent(SystemUI systemUi, Context context) {
+        return new VolumeDialogComponent(systemUi, context);
+    }
+
     public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
             Context context) {
         providers.put(StatusBarStateController.class, StatusBarStateController::new);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index c0047c0..a90a7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -21,7 +21,7 @@
 import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -52,15 +52,20 @@
     private static final int MSG_BUTTON_NEGATIVE = 6;
     private static final int MSG_USER_CANCELED = 7;
     private static final int MSG_BUTTON_POSITIVE = 8;
+    private static final int MSG_BIOMETRIC_SHOW_TRY_AGAIN = 9;
+    private static final int MSG_TRY_AGAIN_PRESSED = 10;
 
     private Map<Integer, BiometricDialogView> mDialogs; // BiometricAuthenticator type, view
     private SomeArgs mCurrentDialogArgs;
     private BiometricDialogView mCurrentDialog;
     private WindowManager mWindowManager;
-    private IBiometricPromptReceiver mReceiver;
+    private IBiometricServiceReceiverInternal mReceiver;
     private boolean mDialogShowing;
     private Callback mCallback = new Callback();
 
+    private boolean mTryAgainShowing; // No good place to save state before config change :/
+    private boolean mConfirmShowing; // No good place to save state before config change :/
+
     private Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -89,6 +94,15 @@
                 case MSG_BUTTON_POSITIVE:
                     handleButtonPositive();
                     break;
+                case MSG_BIOMETRIC_SHOW_TRY_AGAIN:
+                    handleShowTryAgain();
+                    break;
+                case MSG_TRY_AGAIN_PRESSED:
+                    handleTryAgainPressed();
+                    break;
+                default:
+                    Log.w(TAG, "Unknown message: " + msg.what);
+                    break;
             }
         }
     };
@@ -96,7 +110,7 @@
     private class Callback implements DialogViewCallback {
         @Override
         public void onUserCanceled() {
-            mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+            mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
         }
 
         @Override
@@ -107,12 +121,17 @@
 
         @Override
         public void onNegativePressed() {
-            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+            mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
         }
 
         @Override
         public void onPositivePressed() {
-            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+            mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
+        }
+
+        @Override
+        public void onTryAgainPressed() {
+            mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
         }
     }
 
@@ -139,13 +158,14 @@
     }
 
     @Override
-    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
-            boolean requireConfirmation, int userId) {
+    public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+            int type, boolean requireConfirmation, int userId) {
         if (DEBUG) Log.d(TAG, "showBiometricDialog, type: " + type);
         // Remove these messages as they are part of the previous client
         mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
         mHandler.removeMessages(MSG_BIOMETRIC_HELP);
         mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
+        mHandler.removeMessages(MSG_HIDE_DIALOG);
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = bundle;
         args.arg2 = receiver;
@@ -179,6 +199,12 @@
         mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
     }
 
+    @Override
+    public void showBiometricTryAgain() {
+        if (DEBUG) Log.d(TAG, "showBiometricTryAgain");
+        mHandler.obtainMessage(MSG_BIOMETRIC_SHOW_TRY_AGAIN).sendToTarget();
+    }
+
     private void handleShowDialog(SomeArgs args, boolean skipAnimation) {
         mCurrentDialogArgs = args;
         final int type = args.argi1;
@@ -193,11 +219,13 @@
             Log.w(TAG, "Dialog already showing");
             return;
         }
-        mReceiver = (IBiometricPromptReceiver) args.arg2;
+        mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
         mCurrentDialog.setBundle((Bundle)args.arg1);
         mCurrentDialog.setRequireConfirmation((boolean) args.arg3);
         mCurrentDialog.setUserId(args.argi2);
         mCurrentDialog.setSkipIntro(skipAnimation);
+        mCurrentDialog.setPendingTryAgain(mTryAgainShowing);
+        mCurrentDialog.setPendingConfirm(mConfirmShowing);
         mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
         mDialogShowing = true;
     }
@@ -209,7 +237,8 @@
                 mContext.getResources()
                         .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
         if (mCurrentDialog.requiresConfirmation()) {
-            mCurrentDialog.showConfirmationButton();
+            mConfirmShowing = true;
+            mCurrentDialog.showConfirmationButton(true /* show */);
         } else {
             handleHideDialog(false /* userCanceled */);
         }
@@ -226,6 +255,7 @@
             if (DEBUG) Log.d(TAG, "Dialog already dismissed");
             return;
         }
+        mTryAgainShowing = false;
         mCurrentDialog.showErrorMessage(error);
     }
 
@@ -246,6 +276,8 @@
         }
         mReceiver = null;
         mDialogShowing = false;
+        mConfirmShowing = false;
+        mTryAgainShowing = false;
         mCurrentDialog.startDismiss();
     }
 
@@ -259,6 +291,7 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling negative button", e);
         }
+        mTryAgainShowing = false;
         handleHideDialog(false /* userCanceled */);
     }
 
@@ -272,13 +305,31 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling positive button", e);
         }
+        mConfirmShowing = false;
         handleHideDialog(false /* userCanceled */);
     }
 
     private void handleUserCanceled() {
+        mTryAgainShowing = false;
+        mConfirmShowing = false;
         handleHideDialog(true /* userCanceled */);
     }
 
+    private void handleShowTryAgain() {
+        mCurrentDialog.showTryAgainButton(true /* show */);
+        mTryAgainShowing = true;
+    }
+
+    private void handleTryAgainPressed() {
+        try {
+            mCurrentDialog.clearTemporaryMessage();
+            mTryAgainShowing = false;
+            mReceiver.onTryAgainPressed();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException when handling try again", e);
+        }
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 38427ad..e085f23 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -87,6 +87,9 @@
     protected boolean mRequireConfirmation;
     private int mUserId; // used to determine if we should show work background
 
+    private boolean mPendingShowTryAgain;
+    private boolean mPendingShowConfirm;
+
     protected abstract void updateIcon(int lastState, int newState);
     protected abstract int getHintStringResourceId();
     protected abstract int getAuthenticatedAccessibilityResourceId();
@@ -178,6 +181,7 @@
         final Button negative = mLayout.findViewById(R.id.button2);
         final Button positive = mLayout.findViewById(R.id.button1);
         final ImageView icon = mLayout.findViewById(R.id.biometric_icon);
+        final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
 
         icon.setContentDescription(getResources().getString(getIconDescriptionResourceId()));
 
@@ -193,6 +197,11 @@
             mCallback.onPositivePressed();
         });
 
+        tryAgain.setOnClickListener((View v) -> {
+            showTryAgainButton(false /* show */);
+            mCallback.onTryAgainPressed();
+        });
+
         mLayout.setFocusableInTouchMode(true);
         mLayout.requestFocus();
     }
@@ -207,7 +216,6 @@
         final TextView subtitle = mLayout.findViewById(R.id.subtitle);
         final TextView description = mLayout.findViewById(R.id.description);
         final Button negative = mLayout.findViewById(R.id.button2);
-        final Button positive = mLayout.findViewById(R.id.button1);
         final ImageView backgroundView = mLayout.findViewById(R.id.background);
 
         if (mUserManager.isManagedProfile(mUserId)) {
@@ -233,8 +241,6 @@
         title.setText(titleText);
         title.setSelected(true);
 
-        positive.setVisibility(View.INVISIBLE);
-
         final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
         if (TextUtils.isEmpty(subtitleText)) {
             subtitle.setVisibility(View.GONE);
@@ -243,7 +249,8 @@
             subtitle.setText(subtitleText);
         }
 
-        final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+        final CharSequence descriptionText =
+                mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
         if (TextUtils.isEmpty(descriptionText)) {
             description.setVisibility(View.GONE);
         } else {
@@ -253,6 +260,9 @@
 
         negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
+        showTryAgainButton(mPendingShowTryAgain);
+        showConfirmationButton(mPendingShowConfirm);
+
         if (mWasForceRemoved || mSkipIntro) {
             // Show the dialog immediately
             mLayout.animate().cancel();
@@ -281,11 +291,17 @@
     public void startDismiss() {
         mAnimatingAway = true;
 
+        // This is where final cleanup should occur.
         final Runnable endActionRunnable = new Runnable() {
             @Override
             public void run() {
                 mWindowManager.removeView(BiometricDialogView.this);
                 mAnimatingAway = false;
+                // Set the icons / text back to normal state
+                handleClearMessage();
+                showTryAgainButton(false /* show */);
+                mPendingShowTryAgain = false;
+                mPendingShowConfirm = false;
             }
         };
 
@@ -345,9 +361,13 @@
         return mRequireConfirmation;
     }
 
-    public void showConfirmationButton() {
+    public void showConfirmationButton(boolean show) {
         final Button positive = mLayout.findViewById(R.id.button1);
-        positive.setVisibility(View.VISIBLE);
+        if (show) {
+            positive.setVisibility(View.VISIBLE);
+        } else {
+            positive.setVisibility(View.GONE);
+        }
     }
 
     public void setUserId(int userId) {
@@ -376,12 +396,18 @@
                 BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
+    public void clearTemporaryMessage() {
+        mHandler.removeMessages(MSG_CLEAR_MESSAGE);
+        mHandler.obtainMessage(MSG_CLEAR_MESSAGE).sendToTarget();
+    }
+
     public void showHelpMessage(String message) {
         showTemporaryMessage(message);
     }
 
     public void showErrorMessage(String error) {
         showTemporaryMessage(error);
+        showTryAgainButton(false /* show */);
         mCallback.onErrorShown();
     }
 
@@ -390,6 +416,25 @@
         mLastState = newState;
     }
 
+    public void showTryAgainButton(boolean show) {
+        final Button tryAgain = mLayout.findViewById(R.id.button_try_again);
+        if (show) {
+            tryAgain.setVisibility(View.VISIBLE);
+        } else {
+            tryAgain.setVisibility(View.GONE);
+        }
+    }
+
+    // Set the state before the window is attached, so we know if the dialog should be started
+    // with or without the button. This is because there's no good onPause signal
+    public void setPendingTryAgain(boolean show) {
+        mPendingShowTryAgain = show;
+    }
+
+    public void setPendingConfirm(boolean show) {
+        mPendingShowConfirm = show;
+    }
+
     public WindowManager.LayoutParams getLayoutParams() {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index f388d9c..24fd22e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -43,4 +43,9 @@
      * should be dismissed.
      */
     void onPositivePressed();
+
+    /**
+     * Invoked when the "try again" button is pressed.
+     */
+    void onTryAgainPressed();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index e868f96..416cc59 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,10 +23,10 @@
 import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
 
 import android.app.Notification;
-import android.app.NotificationManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -57,6 +57,11 @@
     // When a bubble is dismissed, recreate it as a notification
     public static final boolean DEBUG_DEMOTE_TO_NOTIF = false;
 
+    // Secure settings
+    private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
+    private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
+    private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+
     private Context mContext;
     private BubbleDismissListener mDismissListener;
     private BubbleStateChangeListener mStateChangeListener;
@@ -268,7 +273,7 @@
         for (BubbleView bv : mBubbles.values()) {
             NotificationData.Entry entry = bv.getEntry();
             if (entry != null) {
-                if (entry.row.isRemoved() || entry.isBubbleDismissed() || entry.row.isDismissed()) {
+                if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
                     viewsToRemove.add(bv);
                 }
             }
@@ -318,11 +323,15 @@
     /**
      * Whether the notification should bubble or not.
      */
-    public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority,
-            boolean canAppOverlay) {
-        if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) {
+    public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+        if (entry.isBubbleDismissed()) {
             return false;
         }
+
+        boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+        boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+        boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE;
+
         StatusBarNotification n = entry.notification;
         boolean hasRemoteInput = false;
         if (n.getNotification().actions != null) {
@@ -333,12 +342,28 @@
                 }
             }
         }
+
         Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle();
-        boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH
-                || Notification.MessagingStyle.class.equals(style)
+        boolean isMessageType = Notification.MessagingStyle.class.equals(style)
                 || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category)
-                || hasRemoteInput
-                || canAppOverlay;
-        return shouldBubble && !entry.isBubbleDismissed();
+                || hasRemoteInput;
+        return (isMessageType && autoBubbleMessages)
+                || (n.isOngoing() && autoBubbleOngoing)
+                || autoBubbleAll;
+    }
+
+    private static boolean shouldAutoBubbleMessages(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0;
+    }
+
+    private static boolean shouldAutoBubbleOngoing(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0;
+    }
+
+    private static boolean shouldAutoBubbleAll(Context context) {
+        return Settings.Secure.getInt(context.getContentResolver(),
+                ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index a79e047..6c47aac 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -120,7 +120,7 @@
      * @return the view to display when the bubble is expanded.
      */
     public ExpandableNotificationRow getRowView() {
-        return mEntry.row;
+        return mEntry.getRow();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
deleted file mode 100644
index 09c000b..0000000
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.car;
-
-import android.content.Context;
-import android.util.ArrayMap;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.Dependency.DependencyProvider;
-import com.android.systemui.SystemUIFactory;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.car.CarFacetButtonController;
-import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.car.hvac.HvacController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-
-/**
- * Class factory to provide car specific SystemUI components.
- */
-public class CarSystemUIFactory extends SystemUIFactory {
-
-    public StatusBarKeyguardViewManager createStatusBarKeyguardViewManager(Context context,
-            ViewMediatorCallback viewMediatorCallback, LockPatternUtils lockPatternUtils) {
-        return new CarStatusBarKeyguardViewManager(context, viewMediatorCallback, lockPatternUtils);
-    }
-
-    @Override
-    public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-            Context context) {
-        super.injectDependencies(providers, context);
-        providers.put(NotificationEntryManager.class,
-                () -> new CarNotificationEntryManager(context));
-        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
-        providers.put(HvacController.class, () -> new HvacController(context));
-        providers.put(NotificationMediaManager.class,
-                () -> new CarNotificationMediaManager(context));
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 2c61da3..1718cff 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -108,7 +108,7 @@
                 UserHandle.USER_ALL);
 
         updateConfiguration();
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
     }
 
     public static FalsingManager getInstance(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 01a2345..1dd3101 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -24,20 +24,21 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-import android.os.Build;
 import android.os.Handler;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
 
 /**
  * Controls the screen brightness when dozing.
  */
 public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachine.Part,
         SensorEventListener {
+    private static final boolean DEBUG_AOD_BRIGHTNESS = SystemProperties
+            .getBoolean("debug.aod_brightness", false);
     protected static final String ACTION_AOD_BRIGHTNESS =
             "com.android.systemui.doze.AOD_BRIGHTNESS";
     protected static final String BRIGHTNESS_BUCKET = "brightness_bucket";
@@ -83,11 +84,9 @@
         mSensorToScrimOpacity = sensorToScrimOpacity;
 
         if (mDebuggable) {
-            Dependency.get(Dependency.BG_HANDLER).post(()-> {
-                IntentFilter filter = new IntentFilter();
-                filter.addAction(ACTION_AOD_BRIGHTNESS);
-                mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
-            });
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ACTION_AOD_BRIGHTNESS);
+            mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
         }
     }
 
@@ -97,7 +96,7 @@
         this(context, service, sensorManager, lightSensor, host, handler,
                 context.getResources().getInteger(
                         com.android.internal.R.integer.config_screenBrightnessDoze),
-                policy.screenBrightnessArray, policy.dimmingScrimArray, Build.IS_DEBUGGABLE);
+                policy.screenBrightnessArray, policy.dimmingScrimArray, DEBUG_AOD_BRIGHTNESS);
     }
 
     @Override
@@ -126,9 +125,7 @@
     private void onDestroy() {
         setLightSensorEnabled(false);
         if (mDebuggable) {
-            Dependency.get(Dependency.BG_HANDLER).post(()-> {
-                mContext.unregisterReceiver(this);
-            });
+            mContext.unregisterReceiver(this);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bf8e04d..7e77843 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -401,7 +401,9 @@
                 }
                 mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY,
                         event.values);
-                updateListener();  // reregister, this sensor only fires once
+                if (!mRegistered) {
+                    updateListener();  // reregister, this sensor only fires once
+                }
             }));
         }
 
@@ -440,7 +442,9 @@
                 mRegistered = false;
                 mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1,
                         event.getValues());
-                updateListener();  // reregister, this sensor only fires once
+                if (!mRegistered) {
+                    updateListener();  // reregister, this sensor only fires once
+                }
             }));
         };
 
@@ -487,7 +491,6 @@
             }
             return sb.append(']').toString();
         }
-
     }
 
     public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index bad0148..afe9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -184,6 +184,7 @@
             if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
             mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
         }
+
         if (far && (paused || pausing)) {
             if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
             mMachine.requestState(DozeMachine.State.DOZE_AOD);
@@ -205,13 +206,13 @@
                     // In pocket, drop event.
                     return;
                 }
-                if (pausing || paused) {
+                if (mMachine.getState() == DozeMachine.State.DOZE) {
                     mMachine.requestState(DozeMachine.State.DOZE_AOD);
                 }
             }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
         } else {
             if (!pausing && !paused) {
-                mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+                mMachine.requestState(DozeMachine.State.DOZE);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
index 9a5a5b8..be504ef 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java
@@ -23,6 +23,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.DozeParameters;
 
 import java.io.PrintWriter;
@@ -80,11 +81,12 @@
         if (isAmbientMode != mIsAmbientMode) {
             mIsAmbientMode = isAmbientMode;
             try {
+                long duration = animated ? StackStateAnimator.ANIMATION_DURATION_WAKEUP : 0L;
                 if (DEBUG) {
                     Log.i(TAG, "AOD wallpaper state changed to: " + mIsAmbientMode
-                            + ", animated: " + animated);
+                            + ", animationDuration: " + duration);
                 }
-                mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, animated);
+                mWallpaperManagerService.setInAmbientMode(mIsAmbientMode, duration);
             } catch (RemoteException e) {
                 // Cannot notify wallpaper manager service, but it's fine, let's just skip it.
                 Log.w(TAG, "Cannot notify state to WallpaperManagerService: " + mIsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index e447def..8495fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,6 +19,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
 
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
@@ -65,6 +67,7 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -90,8 +93,8 @@
     public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
     public static final int MESSAGE_ANIMATION_ENDED = 6;
 
-    private static final long INITIAL_DISMISS_DELAY = 3500;
-    private static final long POST_INTERACTION_DISMISS_DELAY = 2000;
+    private static final int INITIAL_DISMISS_DELAY = 3500;
+    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
     private static final long MENU_FADE_DURATION = 125;
 
     private static final float MENU_BACKGROUND_ALPHA = 0.3f;
@@ -105,6 +108,7 @@
 
     private final List<RemoteAction> mActions = new ArrayList<>();
 
+    private AccessibilityManager mAccessibilityManager;
     private View mViewRoot;
     private Drawable mBackgroundDrawable;
     private View mMenuContainer;
@@ -194,6 +198,7 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.pip_menu_activity);
 
+        mAccessibilityManager = getSystemService(AccessibilityManager.class);
         mBackgroundDrawable = new ColorDrawable(Color.BLACK);
         mBackgroundDrawable.setAlpha(0);
         mViewRoot = findViewById(R.id.background);
@@ -639,8 +644,10 @@
         mHandler.removeCallbacks(mFinishRunnable);
     }
 
-    private void repostDelayedFinish(long delay) {
+    private void repostDelayedFinish(int delay) {
+        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
         mHandler.removeCallbacks(mFinishRunnable);
-        mHandler.postDelayed(mFinishRunnable, delay);
+        mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
     }
 }
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 03a573e..6a9f24c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -368,7 +368,7 @@
 
     private void onAccessibilityShowMenu() {
         mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
-                mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
+                mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
     }
 
     private boolean handleTouchEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index f6a95af..bbdae29 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -20,6 +20,7 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.content.res.ColorStateList
+import android.view.Gravity
 import android.view.LayoutInflater
 import android.view.View
 import android.widget.ImageView
@@ -34,13 +35,13 @@
     val dialogBuilder: PrivacyDialogBuilder
 ) {
 
-    val iconSize = context.resources.getDimensionPixelSize(
-            R.dimen.ongoing_appops_dialog_icon_height)
-    val iconColor = context.resources.getColor(
+    private val iconSize = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_icon_size)
+    private val iconColor = context.resources.getColor(
             com.android.internal.R.color.text_color_primary, context.theme)
-    companion object {
-        private const val MAX_ITEMS = 10
-    }
+    private val iconMargin = context.resources.getDimensionPixelSize(
+            R.dimen.ongoing_appops_dialog_icon_margin)
+    private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps)
 
     fun createDialog(): Dialog {
         val builder = AlertDialog.Builder(context).apply {
@@ -105,6 +106,11 @@
         val appName = item.findViewById(R.id.app_name) as TextView
         val icons = item.findViewById(R.id.icons) as LinearLayout
 
+        var lp = LinearLayout.LayoutParams(iconSize, iconSize).apply {
+            gravity = Gravity.CENTER_VERTICAL
+            marginStart = iconMargin
+        }
+
         app.icon?.let {
             appIcon.setImageDrawable(it)
         }
@@ -117,7 +123,7 @@
                     imageTintList = ColorStateList.valueOf(iconColor)
                     setImageDrawable(it)
                 }
-                icons.addView(image, iconSize, LinearLayout.LayoutParams.WRAP_CONTENT)
+                icons.addView(image, lp)
             }
             icons.visibility = View.VISIBLE
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 8b434a5..496aa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -323,7 +323,9 @@
             post(new Runnable() {
                 @Override
                 public void run() {
-                    handleShowingDetail(detail, x, y, false /* toggleQs */);
+                    if (isAttachedToWindow()) {
+                        handleShowingDetail(detail, x, y, false /* toggleQs */);
+                    }
                 }
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 591e9e0..9d2be39 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -26,6 +26,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Handler;
+import android.provider.Settings;
 import android.service.quicksettings.TileService;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -69,7 +70,8 @@
         mSpecs.clear();
         mFinished = false;
         // Enqueue jobs to fetch every system tile and then ever package tile.
-        addStockTiles(host);
+        addCurrentAndStockTiles(host);
+
         addPackageTiles(host);
     }
 
@@ -77,16 +79,28 @@
         return mFinished;
     }
 
-    private void addStockTiles(QSTileHost host) {
-        String possible = mContext.getString(R.string.quick_settings_tiles_stock);
+    private void addCurrentAndStockTiles(QSTileHost host) {
+        String stock = mContext.getString(R.string.quick_settings_tiles_stock);
+        String current = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.QS_TILES);
         final ArrayList<String> possibleTiles = new ArrayList<>();
-        possibleTiles.addAll(Arrays.asList(possible.split(",")));
-        if (Build.IS_DEBUGGABLE) {
+        if (current != null) {
+            // The setting QS_TILES is not populated immediately upon Factory Reset
+            possibleTiles.addAll(Arrays.asList(current.split(",")));
+        }
+        String[] stockSplit =  stock.split(",");
+        for (String spec : stockSplit) {
+            if (!current.contains(spec)) {
+                possibleTiles.add(spec);
+            }
+        }
+        if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {
             possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
         }
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
         for (String spec : possibleTiles) {
+            // Only add current and stock tiles that can be created from QSFactoryImpl
             final QSTile tile = host.createTile(spec);
             if (tile == null) {
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index b2f6043..7d52f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -114,7 +114,7 @@
         }
 
         // Broken tiles.
-        Log.w(TAG, "Bad tile spec: " + tileSpec);
+        Log.w(TAG, "No stock tile spec: " + tileSpec);
         return null;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 0638998..3a96595d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -198,7 +198,8 @@
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
 
-        mAccessibilityClass = state.expandedAccessibilityClassName;
+        mAccessibilityClass =
+                state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
         if (state instanceof QSTile.BooleanState) {
             boolean newState = ((BooleanState) state).value;
             if (mTileState != newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index c62a592..3ab1c21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -205,7 +205,10 @@
             } else {
                 final BluetoothClass bluetoothClass = lastDevice.getBtClass();
                 if (bluetoothClass != null) {
-                    if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+                    if (lastDevice.isHearingAidDevice()) {
+                        return mContext.getString(
+                                R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
+                    } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
                         return mContext.getString(
                                 R.string.quick_settings_bluetooth_secondary_label_audio);
                     } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b89324..12b6f67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
@@ -97,6 +98,7 @@
     private int mCurrentBoundedUserId = -1;
     private float mBackButtonAlpha;
     private MotionEvent mStatusBarGestureDownEvent;
+    private float mWindowCornerRadius;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
@@ -228,6 +230,18 @@
             }
         }
 
+        public float getWindowCornerRadius() {
+            if (!verifyCaller("getWindowCornerRadius")) {
+                return 0;
+            }
+            long token = Binder.clearCallingIdentity();
+            try {
+                return mWindowCornerRadius;
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
@@ -334,6 +348,7 @@
                 .setPackage(mRecentsComponentName.getPackageName());
         mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
                 getDefaultInteractionFlags());
+        mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
 
         // Listen for the package update changes.
         if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 216b940..f796793 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -219,7 +219,7 @@
             mLayout.findViewById(R.id.screen_pinning_text_area)
                     .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             View buttons = mLayout.findViewById(R.id.screen_pinning_buttons);
-            if (WindowManagerWrapper.getInstance().hasSoftNavigationBar()) {
+            if (WindowManagerWrapper.getInstance().hasSoftNavigationBar(mContext.getDisplayId())) {
                 buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
                 swapChildrenIfRtlAndVertical(buttons);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 3da6d2e..bc38169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -112,8 +112,7 @@
             return;
         }
 
-        alertEntry.mEntry.row.sendAccessibilityEvent(
-                AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        alertEntry.mEntry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         if (alert) {
             alertEntry.updateEntry(true /* updatePostTime */);
         }
@@ -186,7 +185,7 @@
         alertEntry.setEntry(entry);
         mAlertEntries.put(entry.key, alertEntry);
         onAlertEntryAdded(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
     /**
@@ -207,7 +206,7 @@
         Entry entry = alertEntry.mEntry;
         mAlertEntries.remove(key);
         onAlertEntryRemoved(alertEntry);
-        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         alertEntry.reset();
         if (mExtendedLifetimeAlertEntries.contains(entry)) {
             if (mNotificationLifetimeFinishedCallback != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index f1c0304..8821679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -33,7 +33,7 @@
  * dozing and/or in AOD.  The pulse uses the notification's ambient view and pops in briefly
  * before automatically dismissing the alert.
  */
-public final class AmbientPulseManager extends AlertingNotificationManager {
+public class AmbientPulseManager extends AlertingNotificationManager {
 
     protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>();
     @VisibleForTesting
@@ -79,7 +79,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(true);
+        entry.setAmbientPulsing(true);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, true);
         }
@@ -88,11 +88,11 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setAmbientPulsing(false);
+        entry.setAmbientPulsing(false);
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 0c8f487..b550274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,7 +21,7 @@
 import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -96,6 +96,7 @@
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_TRY_AGAIN           = 47 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -163,12 +164,13 @@
 
         default void onRotationProposal(int rotation, boolean isValid) { }
 
-        default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver,
+        default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
                 int type, boolean requireConfirmation, int userId) { }
         default void onBiometricAuthenticated() { }
         default void onBiometricHelp(String message) { }
         default void onBiometricError(String error) { }
         default void hideBiometricDialog() { }
+        default void showBiometricTryAgain() { }
     }
 
     @VisibleForTesting
@@ -221,7 +223,9 @@
         }
     }
 
-    public void disable(int state1, int state2) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void disable(int displayId, int state1, int state2) {
         disable(state1, state2, true);
     }
 
@@ -264,8 +268,10 @@
         }
     }
 
-    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
-            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
         synchronized (mLock) {
             // Don't coalesce these, since it might have one time flags set such as
             // STATUS_BAR_UNHIDE which might get lost.
@@ -280,7 +286,9 @@
         }
     }
 
-    public void topAppWindowChanged(boolean menuVisible) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void topAppWindowChanged(int displayId, boolean menuVisible) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED);
             mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0,
@@ -288,7 +296,9 @@
         }
     }
 
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
             boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -369,7 +379,9 @@
         }
     }
 
-    public void setWindowState(int window, int state) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void setWindowState(int displayId, int window, int state) {
         synchronized (mLock) {
             // don't coalesce these
             mHandler.obtainMessage(MSG_SET_WINDOW_STATE, window, state, null).sendToTarget();
@@ -383,7 +395,9 @@
         }
     }
 
-    public void appTransitionPending() {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionPending(int displayId) {
         appTransitionPending(false /* forced */);
     }
 
@@ -393,13 +407,17 @@
         }
     }
 
-    public void appTransitionCancelled() {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionCancelled(int displayId) {
         synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_CANCELLED);
         }
     }
 
-    public void appTransitionStarting(long startTime, long duration) {
+    // TODO(b/117478341): Add multi-display support.
+    @Override
+    public void appTransitionStarting(int displayId, long startTime, long duration) {
         appTransitionStarting(startTime, duration, false /* forced */);
     }
 
@@ -410,8 +428,9 @@
         }
     }
 
+    // TODO(b/117478341): Add multi-display support.
     @Override
-    public void appTransitionFinished() {
+    public void appTransitionFinished(int displayId) {
         synchronized (mLock) {
             mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED);
         }
@@ -523,8 +542,8 @@
     }
 
     @Override
-    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
-            boolean requireConfirmation, int userId) {
+    public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+            int type, boolean requireConfirmation, int userId) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
@@ -565,6 +584,13 @@
         }
     }
 
+    @Override
+    public void showBiometricTryAgain() {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_BIOMETRIC_TRY_AGAIN).sendToTarget();
+        }
+    }
+
     private final class H extends Handler {
         private H(Looper l) {
             super(l);
@@ -774,7 +800,7 @@
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).showBiometricDialog(
                                 (Bundle) someArgs.arg1,
-                                (IBiometricPromptReceiver) someArgs.arg2,
+                                (IBiometricServiceReceiverInternal) someArgs.arg2,
                                 someArgs.argi1 /* type */,
                                 (boolean) someArgs.arg3 /* requireConfirmation */,
                                 someArgs.argi2 /* userId */);
@@ -816,6 +842,11 @@
                         mCallbacks.get(i).showPinningEscapeToast();
                     }
                     break;
+                case MSG_BIOMETRIC_TRY_AGAIN:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).showBiometricTryAgain();
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
index 3b611a3..2f26109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DisplayNavigationBarController.java
@@ -22,8 +22,11 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.IWindowManager;
 import android.view.View;
 import android.view.WindowManagerGlobal;
 
@@ -34,6 +37,8 @@
  */
 public class DisplayNavigationBarController implements DisplayListener {
 
+    private static final String TAG = DisplayNavigationBarController.class.getName();
+
     private final Context mContext;
     private final Handler mHandler;
     private final DisplayManager mDisplayManager;
@@ -112,14 +117,23 @@
         }
 
         final int displayId = display.getDisplayId();
+        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+        try {
+            if (!wms.hasNavigationBar(displayId)) {
+                return;
+            }
+        } catch (RemoteException e) {
+            // Cannot get wms, just return with warning message.
+            Log.w(TAG, "Cannot get WindowManager.");
+            return;
+        }
         final Context externalDisplayContext = mContext.createDisplayContext(display);
-        NavigationBarFragment.create(externalDisplayContext,
-                (tag, fragment) -> {
-                    final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
-                    // TODO(b/115978725): handle external nav bars sysuiVisibility
-                    navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
-                    mExternalNavigationBarMap.append(displayId, navBar);
-                }
-        );
+        NavigationBarFragment.create(externalDisplayContext, (tag, fragment) -> {
+            final NavigationBarFragment navBar = (NavigationBarFragment) fragment;
+            // TODO(b/115978725): handle external nav bars sysuiVisibility
+            navBar.setCurrentSysuiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+            mExternalNavigationBarMap.append(displayId, navBar);
+        });
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 745b2f9..b9684fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -27,7 +27,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 public class EmptyShadeView extends StackScrollerDecorView {
 
@@ -74,7 +73,7 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    public ExpandableViewState createExpandableViewState() {
         return new EmptyShadeViewState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 960d221..7d1b640 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -156,7 +156,7 @@
                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
                 Dependency.get(Dependency.TIME_TICK_HANDLER));
 
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
@@ -167,7 +167,7 @@
      */
     public void destroy() {
         mContext.unregisterReceiver(mTickReceiver);
-        Dependency.get(StatusBarStateController.class).removeListener(this);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index b0724b1..bba4369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -165,7 +165,7 @@
         mCurrentUserId = ActivityManager.getCurrentUser();
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
         mLockPatternUtils = new LockPatternUtils(context);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fc1e94a..f045548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -199,7 +199,7 @@
             for (int i = 0; i < N; i++) {
                 final NotificationData.Entry entry = activeNotifications.get(i);
 
-                if (isMediaNotification(entry)) {
+                if (entry.isMediaNotification()) {
                     final MediaSession.Token token =
                             entry.notification.getNotification().extras.getParcelable(
                                     Notification.EXTRA_MEDIA_SESSION);
@@ -336,13 +336,6 @@
         return PlaybackState.STATE_NONE;
     }
 
-    private boolean isMediaNotification(NotificationData.Entry entry) {
-        // TODO: confirm that there's a valid media key
-        return entry.row.getExpandedContentView() != null
-                && entry.row.getExpandedContentView().findViewById(
-                        com.android.internal.R.id.media_actions) != null;
-    }
-
     private void clearCurrentMediaNotificationSession() {
         mMediaMetadata = null;
         if (mMediaController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 8c53cc2..ecadf96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -141,7 +141,7 @@
                 ActivityManager.getService().resumeAppSwitches();
             } catch (RemoteException e) {
             }
-            return mCallback.handleRemoteViewClick(pendingIntent, () -> {
+            return mCallback.handleRemoteViewClick(view, pendingIntent, () -> {
                 Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                 options.second.setLaunchWindowingMode(
                         WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
@@ -150,34 +150,42 @@
         }
 
         private void logActionClick(View view) {
+            Integer actionIndex = (Integer)
+                    view.getTag(com.android.internal.R.id.notification_action_index_tag);
+            if (actionIndex == null) {
+                Log.e(TAG, "Couldn't retrieve the actionIndex from the clicked button");
+                return;
+            }
             ViewParent parent = view.getParent();
-            String key = getNotificationKeyForParent(parent);
-            if (key == null) {
+            StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+            if (statusBarNotification == null) {
                 Log.w(TAG, "Couldn't determine notification for click.");
                 return;
             }
-            int index = -1;
+            String key = statusBarNotification.getKey();
+            int buttonIndex = -1;
             // If this is a default template, determine the index of the button.
             if (view.getId() == com.android.internal.R.id.action0 &&
                     parent != null && parent instanceof ViewGroup) {
                 ViewGroup actionGroup = (ViewGroup) parent;
-                index = actionGroup.indexOfChild(view);
+                buttonIndex = actionGroup.indexOfChild(view);
             }
             final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
             final int rank = mEntryManager.getNotificationData().getRank(key);
+            final Notification.Action action =
+                    statusBarNotification.getNotification().actions[actionIndex];
             final NotificationVisibility nv = NotificationVisibility.obtain(key, rank, count, true);
             try {
-                mBarService.onNotificationActionClick(key, index, nv);
+                mBarService.onNotificationActionClick(key, buttonIndex, action, nv, false);
             } catch (RemoteException e) {
                 // Ignore
             }
         }
 
-        private String getNotificationKeyForParent(ViewParent parent) {
+        private StatusBarNotification getNotificationForParent(ViewParent parent) {
             while (parent != null) {
                 if (parent instanceof ExpandableNotificationRow) {
-                    return ((ExpandableNotificationRow) parent)
-                            .getStatusBarNotification().getKey();
+                    return ((ExpandableNotificationRow) parent).getStatusBarNotification();
                 }
                 parent = parent.getParent();
             }
@@ -393,7 +401,7 @@
     }
 
     public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -403,7 +411,7 @@
     }
 
     public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
-        if (entry.row == null || entry.row.isDismissed()) {
+        if (entry.isDismissed()) {
             return false;
         }
         if (!FORCE_REMOTE_INPUT_HISTORY) {
@@ -532,7 +540,7 @@
 
                 // Ensure the entry hasn't already been removed. This can happen if there is an
                 // inflation exception while updating the remote history
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -570,7 +578,7 @@
 
                 mEntryManager.updateNotification(newSbn, null);
 
-                if (entry.row == null || entry.row.isRemoved()) {
+                if (entry.isRemoved()) {
                     return;
                 }
 
@@ -593,7 +601,7 @@
     protected class RemoteInputActiveExtender extends RemoteInputExtender {
         @Override
         public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
-            if (entry.row == null || entry.row.isDismissed()) {
+            if (entry.isDismissed()) {
                 return false;
             }
             return mRemoteInputController.isRemoteInputActive(entry);
@@ -658,11 +666,13 @@
          * Performs any special handling for a remote view click. The default behaviour can be
          * called through the defaultHandler parameter.
          *
+         * @param view
          * @param pendingIntent
          * @param defaultHandler
          * @return  true iff the click was handled
          */
-        boolean handleRemoteViewClick(PendingIntent pendingIntent, ClickHandler defaultHandler);
+        boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
+                ClickHandler defaultHandler);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 37cc299..6cec36a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 import com.android.systemui.statusbar.notification.stack.ViewState;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
 
@@ -68,7 +67,6 @@
 
     private boolean mDark;
     private NotificationIconContainer mShelfIcons;
-    private ShelfState mShelfState;
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
     private int mIconAppearTopPadding;
@@ -115,7 +113,6 @@
         setClipChildren(false);
         setClipToPadding(false);
         mShelfIcons.setIsStaticLayout(false);
-        mShelfState = new ShelfState();
         setBottomRoundness(1.0f, false /* animate */);
         initDimens();
     }
@@ -124,13 +121,13 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class)
-                .addListener(mStateListener, StatusBarStateController.RANK_SHELF);
+                .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -187,52 +184,53 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        return mShelfState;
+    public ExpandableViewState createExpandableViewState() {
+        return new ShelfState();
     }
 
-    public void updateState(StackScrollState resultState,
-            AmbientState ambientState) {
-        View lastView = ambientState.getLastVisibleBackgroundChild();
+    /** Update the state of the shelf. */
+    public void updateState(AmbientState ambientState) {
+        ExpandableView lastView = ambientState.getLastVisibleBackgroundChild();
+        ShelfState viewState = (ShelfState) getViewState();
         if (mShowNotificationShelf && lastView != null) {
             float maxShelfEnd = ambientState.getInnerHeight() + ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
-            ExpandableViewState lastViewState = resultState.getViewStateForView(lastView);
+            ExpandableViewState lastViewState = lastView.getViewState();
             float viewEnd = lastViewState.yTranslation + lastViewState.height;
-            mShelfState.copyFrom(lastViewState);
-            mShelfState.height = getIntrinsicHeight();
+            viewState.copyFrom(lastViewState);
+            viewState.height = getIntrinsicHeight();
 
-            float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+            float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - viewState.height,
                     getFullyClosedTranslation());
             float darkTranslation = mAmbientState.getDarkTopPadding();
             float yRatio = mAmbientState.hasPulsingNotifications() ?
                     0 : mAmbientState.getDarkAmount();
-            mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
-            mShelfState.zTranslation = ambientState.getBaseZHeight();
+            viewState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
+            viewState.zTranslation = ambientState.getBaseZHeight();
             // For the small display size, it's not enough to make the icon not covered by
             // the top cutout so the denominator add the height of cutout.
             // Totally, (getIntrinsicHeight() * 2 + mCutoutHeight) should be smaller then
             // mAmbientState.getTopPadding().
-            float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
+            float openedAmount = (viewState.yTranslation - getFullyClosedTranslation())
                     / (getIntrinsicHeight() * 2 + mCutoutHeight);
             openedAmount = Math.min(1.0f, openedAmount);
-            mShelfState.openedAmount = openedAmount;
-            mShelfState.clipTopAmount = 0;
-            mShelfState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
-            mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
-            mShelfState.hideSensitive = false;
-            mShelfState.xTranslation = getTranslationX();
+            viewState.openedAmount = openedAmount;
+            viewState.clipTopAmount = 0;
+            viewState.alpha = mAmbientState.hasPulsingNotifications() ? 0 : 1;
+            viewState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
+            viewState.hideSensitive = false;
+            viewState.xTranslation = getTranslationX();
             if (mNotGoneIndex != -1) {
-                mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex);
+                viewState.notGoneIndex = Math.min(viewState.notGoneIndex, mNotGoneIndex);
             }
-            mShelfState.hasItemsInStableShelf = lastViewState.inShelf;
-            mShelfState.hidden = !mAmbientState.isShadeExpanded()
-                    || mAmbientState.isQsCustomizerShowing();
-            mShelfState.maxShelfEnd = maxShelfEnd;
+            viewState.hasItemsInStableShelf = lastViewState.inShelf;
+            viewState.hidden = !mAmbientState.isShadeExpanded()
+                    || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
+            viewState.maxShelfEnd = maxShelfEnd;
         } else {
-            mShelfState.hidden = true;
-            mShelfState.location = ExpandableViewState.LOCATION_GONE;
-            mShelfState.hasItemsInStableShelf = false;
+            viewState.hidden = true;
+            viewState.location = ExpandableViewState.LOCATION_GONE;
+            viewState.hasItemsInStableShelf = false;
         }
     }
 
@@ -261,7 +259,7 @@
         int notGoneIndex = 0;
         int colorOfViewBeforeLast = NO_COLOR;
         boolean backgroundForceHidden = false;
-        if (mHideBackground && !mShelfState.hasItemsInStableShelf) {
+        if (mHideBackground && !((ShelfState) getViewState()).hasItemsInStableShelf) {
             backgroundForceHidden = true;
         }
         int colorTwoBefore = NO_COLOR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index ea67736..dc3a607 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -51,6 +51,7 @@
 public class NotificationViewHierarchyManager {
     private static final String TAG = "NotificationViewHierarchyManager";
 
+    //TODO: change this top <Entry, List<Entry>>?
     private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>>
             mTmpChildOrderMap = new HashMap<>();
 
@@ -128,7 +129,7 @@
                 res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);
         mStatusBarStateListener = new StatusBarStateListener(mBubbleController);
         mEntryManager.setStatusBarStateListener(mStatusBarStateListener);
-        Dependency.get(StatusBarStateController.class).addListener(mStatusBarStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStatusBarStateListener);
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -140,6 +141,7 @@
     /**
      * Updates the visual representation of the notifications.
      */
+    //TODO: Rewrite this to focus on Entries, or some other data object instead of views
     public void updateNotificationViews() {
         ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
                 .getActiveNotifications();
@@ -148,12 +150,12 @@
         final int N = activeNotifications.size();
         for (int i = 0; i < N; i++) {
             NotificationData.Entry ent = activeNotifications.get(i);
-            if (ent.row.isDismissed() || ent.row.isRemoved()) {
+            if (ent.isRowDismissed() || ent.isRowRemoved()) {
                 // we don't want to update removed notifications because they could
                 // temporarily become children if they were isolated before.
                 continue;
             }
-            ent.row.setStatusBarState(mStatusBarStateListener.getCurrentState());
+            ent.getRow().setStatusBarState(mStatusBarStateListener.getCurrentState());
             boolean showAsBubble = ent.isBubble() && !ent.isBubbleDismissed()
                     && mStatusBarStateListener.getCurrentState() == SHADE;
             if (showAsBubble) {
@@ -175,20 +177,19 @@
             boolean deviceSensitive = devicePublic
                     && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
                     mLockscreenUserManager.getCurrentUserId());
-            ent.row.setSensitive(sensitive, deviceSensitive);
-            ent.row.setNeedsRedaction(needsRedaction);
-            if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
-                ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
-                        ent.row.getStatusBarNotification());
+            ent.getRow().setSensitive(sensitive, deviceSensitive);
+            ent.getRow().setNeedsRedaction(needsRedaction);
+            if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
+                NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
                 List<ExpandableNotificationRow> orderedChildren =
-                        mTmpChildOrderMap.get(summary);
+                        mTmpChildOrderMap.get(summary.getRow());
                 if (orderedChildren == null) {
                     orderedChildren = new ArrayList<>();
-                    mTmpChildOrderMap.put(summary, orderedChildren);
+                    mTmpChildOrderMap.put(summary.getRow(), orderedChildren);
                 }
-                orderedChildren.add(ent.row);
+                orderedChildren.add(ent.getRow());
             } else {
-                toShow.add(ent.row);
+                toShow.add(ent.getRow());
             }
 
         }
@@ -391,19 +392,19 @@
                         && !row.isLowPriority()));
             }
 
-            entry.row.setOnAmbient(getShadeController().isDozing());
+            entry.getRow().setOnAmbient(getShadeController().isDozing());
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
-                    entry.notification) && !entry.row.isRemoved();
+                    entry.notification) && !entry.isRowRemoved();
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
                     .notification);
             if (!showOnKeyguard) {
                 // min priority notifications should show if their summary is showing
                 if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
-                    ExpandableNotificationRow summary = mGroupManager.getLogicalGroupSummary(
+                    NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
                             entry.notification);
                     if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
-                            summary.getStatusBarNotification()))         {
+                            summary.notification))         {
                         showOnKeyguard = true;
                     }
                 }
@@ -411,16 +412,16 @@
             if (suppressedSummary
                     || mLockscreenUserManager.shouldHideNotifications(userId)
                     || (onKeyguard && !showOnKeyguard)) {
-                entry.row.setVisibility(View.GONE);
+                entry.getRow().setVisibility(View.GONE);
             } else {
-                boolean wasGone = entry.row.getVisibility() == View.GONE;
+                boolean wasGone = entry.getRow().getVisibility() == View.GONE;
                 if (wasGone) {
-                    entry.row.setVisibility(View.VISIBLE);
+                    entry.getRow().setVisibility(View.VISIBLE);
                 }
-                if (!isChildNotification && !entry.row.isRemoved()) {
+                if (!isChildNotification && !entry.getRow().isRemoved()) {
                     if (wasGone) {
                         // notify the scroller of a child addition
-                        mListContainer.generateAddAnimation(entry.row,
+                        mListContainer.generateAddAnimation(entry.getRow(),
                                 !showOnKeyguard /* fromMoreCard */);
                     }
                     visibleNotifications++;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 929f43e..e8abcc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -250,16 +250,16 @@
         // Make a copy because closing the remote inputs will modify mOpen.
         ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
         for (int i = mOpen.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = mOpen.get(i).first.get();
-            if (item != null && item.row != null) {
-                list.add(item);
+            NotificationData.Entry entry = mOpen.get(i).first.get();
+            if (entry != null && entry.rowExists()) {
+                list.add(entry);
             }
         }
 
         for (int i = list.size() - 1; i >= 0; i--) {
-            NotificationData.Entry item = list.get(i);
-            if (item.row != null) {
-                item.row.closeRemoteInput();
+            NotificationData.Entry entry = list.get(i);
+            if (entry.rowExists()) {
+                entry.closeRemoteInput();
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 37bdc1c..f5d6904 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -15,12 +15,15 @@
  */
 package com.android.systemui.statusbar;
 
+import android.app.Notification;
 import android.os.RemoteException;
 import android.util.ArraySet;
 
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 
 import java.util.Set;
 
@@ -32,6 +35,9 @@
     private IStatusBarService mBarService;
     private Set<String> mSendingKeys = new ArraySet<>();
     private Callback mCallback;
+    private final NotificationEntryManager mEntryManager =
+            Dependency.get(NotificationEntryManager.class);
+
 
     public SmartReplyController() {
         mBarService = Dependency.get(IStatusBarService.class);
@@ -57,6 +63,24 @@
     }
 
     /**
+     * Notifies StatusBarService a smart action is clicked.
+     */
+    public void smartActionClicked(
+            NotificationData.Entry entry, int actionIndex, Notification.Action action,
+            boolean generatedByAssistant) {
+        final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
+        final int rank = mEntryManager.getNotificationData().getRank(entry.key);
+        final NotificationVisibility nv =
+                NotificationVisibility.obtain(entry.key, rank, count, true);
+        try {
+            mBarService.onNotificationActionClick(
+                    entry.key, actionIndex, action, nv, generatedByAssistant);
+        } catch (RemoteException e) {
+            // Nothing to do, system going down
+        }
+    }
+
+    /**
      * Have we posted an intent to an app about sending a smart reply from the
      * notification with this key.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index eaf52cb..3f84416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -26,8 +26,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -36,7 +38,7 @@
 /**
  * Tracks and reports on {@link StatusBarState}.
  */
-public class StatusBarStateController {
+public class StatusBarStateController implements CallbackController<StateListener> {
     private static final String TAG = "SbStateController";
 
     private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
@@ -228,7 +230,7 @@
         return mLastState == StatusBarState.SHADE_LOCKED;
     }
 
-    public void addListener(StateListener listener) {
+    public void addCallback(StateListener listener) {
         synchronized (mListeners) {
             addListenerInternalLocked(listener, Integer.MAX_VALUE);
         }
@@ -244,7 +246,7 @@
      * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
      * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
      */
-    public void addListener(StateListener listener, @SbStateListenerRank int rank) {
+    public void addCallback(StateListener listener, @SbStateListenerRank int rank) {
         synchronized (mListeners) {
             addListenerInternalLocked(listener, rank);
         }
@@ -264,7 +266,7 @@
         mListeners.sort(mComparator);
     }
 
-    public void removeListener(StateListener listener) {
+    public void removeCallback(StateListener listener) {
         synchronized (mListeners) {
             mListeners.removeIf((it) -> it.listener.equals(listener));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index f0e5462..f899863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -29,6 +29,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 
+import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.shared.system.SurfaceControlCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplier;
@@ -55,6 +56,7 @@
     private final NotificationPanelView mNotificationPanel;
     private final NotificationListContainer mNotificationContainer;
     private final StatusBarWindowView mStatusBarWindow;
+    private final float mWindowCornerRadius;
     private Callback mCallback;
     private final Runnable mTimeoutRunnable = () -> {
         setAnimationPending(false);
@@ -72,6 +74,8 @@
         mNotificationContainer = container;
         mStatusBarWindow = statusBarWindow;
         mCallback = callback;
+        mWindowCornerRadius = ScreenDecorationsUtils
+                .getWindowCornerRadius(statusBarWindow.getResources());
     }
 
     public RemoteAnimationAdapter getLaunchAnimation(
@@ -124,6 +128,8 @@
         private final ExpandableNotificationRow mSourceNotification;
         private final ExpandAnimationParameters mParams;
         private final Rect mWindowCrop = new Rect();
+        private final float mNotificationCornerRadius;
+        private float mCornerRadius;
         private boolean mIsFullScreenLaunch = true;
         private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
 
@@ -131,6 +137,8 @@
             mSourceNotification = sourceNofitication;
             mParams = new ExpandAnimationParameters();
             mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
+            mNotificationCornerRadius = Math.max(mSourceNotification.getCurrentTopRoundness(),
+                    mSourceNotification.getCurrentBottomRoundness());
         }
 
         @Override
@@ -181,8 +189,7 @@
                     @Override
                     public void onAnimationUpdate(ValueAnimator animation) {
                         mParams.linearProgress = animation.getAnimatedFraction();
-                        float progress
-                                = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                        float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
                                         mParams.linearProgress);
                         int newWidth = (int) MathUtils.lerp(notificationWidth,
                                 targetWidth, progress);
@@ -194,6 +201,8 @@
                                         + notificationHeight,
                                 primary.position.y + primary.sourceContainerBounds.bottom,
                                 progress);
+                        mCornerRadius = MathUtils.lerp(mNotificationCornerRadius,
+                                mWindowCornerRadius, progress);
                         applyParamsToWindow(primary);
                         applyParamsToNotification(mParams);
                         applyParamsToNotificationList(mParams);
@@ -259,7 +268,7 @@
             m.postTranslate(0, (float) (mParams.top - app.position.y));
             mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
             SurfaceParams params = new SurfaceParams(new SurfaceControlCompat(app.leash),
-                    1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex);
+                    1f /* alpha */, m, mWindowCrop, app.prefixOrderIndex, mCornerRadius);
             mSyncRtTransactionApplier.scheduleApply(params);
         }
 
@@ -290,6 +299,10 @@
             return top;
         }
 
+        public int getBottom() {
+            return bottom;
+        }
+
         public int getWidth() {
             return right - left;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 314a31d..0a2e04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -250,23 +250,24 @@
                     otherChild = null;
                 }
             }
-            if (otherChild == null) {
+            if (otherChild == null && previousTranslation < 0) {
+                // Let's fade out as we approach the top of the screen. We can only do this if
+                // we're actually moving up
                 float distanceToTop = child.getTop() + child.getHeight() + previousTranslation;
                 transformationAmount = distanceToTop / child.getHeight();
                 transformationAmount = Math.max(0.0f, Math.min(1.0f, transformationAmount));
-                if (to) {
-                    transformationAmount = 1.0f - transformationAmount;
-                }
             }
             transformView(transformationAmount, to, child, otherChild, false, /* sameAsAny */
                     useLinearTransformation);
-            if (transformationAmount == 0.0f
-                    && otherGroup.getIsolatedMessage() == otherChild) {
+            boolean otherIsIsolated = otherGroup.getIsolatedMessage() == otherChild;
+            if (transformationAmount == 0.0f && otherIsIsolated) {
                 ownGroup.setTransformingImages(true);
             }
             if (otherChild == null) {
                 child.setTranslationY(previousTranslation);
                 setClippingDeactivated(child, true);
+            } else if (ownGroup.getIsolatedMessage() == child || otherIsIsolated) {
+                // We don't want to add any translation for the image that is transforming
             } else if (to) {
                 float totalTranslation = child.getTop() + ownGroup.getTop()
                         - otherChild.getTop() - otherChild.getTop();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index da6d977..3f8583c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -64,6 +64,8 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -106,7 +108,6 @@
         public int importance;
         public StatusBarIconView icon;
         public StatusBarIconView expandedIcon;
-        public ExpandableNotificationRow row; // the outer expanded view
         private boolean interruption;
         public boolean autoRedacted; // whether the redacted notification was generated by us
         public int targetSdk;
@@ -119,6 +120,10 @@
         public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
         public CharSequence[] smartReplies = new CharSequence[0];
 
+        private Entry parent; // our parent (if we're in a group)
+        private ArrayList<Entry> children = new ArrayList<Entry>();
+        private ExpandableNotificationRow row; // the outer expanded view
+
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
         private InflationTask mRunningTask = null;
@@ -168,7 +173,6 @@
         public void populateFromRanking(@NonNull Ranking ranking) {
             channel = ranking.getChannel();
             audiblyAlerted = ranking.audiblyAlerted();
-            noisy = ranking.isNoisy();
             importance = ranking.getImportance();
             snoozeCriteria = ranking.getSnoozeCriteria();
             userSentiment = ranking.getUserSentiment();
@@ -212,6 +216,24 @@
             }
         }
 
+        public ExpandableNotificationRow getRow() {
+            return row;
+        }
+
+        //TODO: This will go away when we have a way to bind an entry to a row
+        public void setRow(ExpandableNotificationRow row) {
+            this.row = row;
+        }
+
+        @Nullable
+        public List<Entry> getChildren() {
+            if (children.size() <= 0) {
+                return null;
+            }
+
+            return children;
+        }
+
         public void notifyFullScreenIntentLaunched() {
             setInterruption();
             lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
@@ -409,6 +431,198 @@
                 initializationTime = time;
             }
         }
+
+        public void sendAccessibilityEvent(int eventType) {
+            if (row != null) {
+                row.sendAccessibilityEvent(eventType);
+            }
+        }
+
+        /**
+         * Used by NotificationMediaManager to determine... things
+         * @return {@code true} if we are a media notification
+         */
+        public boolean isMediaNotification() {
+            if (row == null) return false;
+
+            return row.isMediaRow();
+        }
+
+        /**
+         * We are a top level child if our parent is the list of notifications duh
+         * @return {@code true} if we're a top level notification
+         */
+        public boolean isTopLevelChild() {
+            return row != null && row.isTopLevelChild();
+        }
+
+        public void resetUserExpansion() {
+            if (row != null) row.resetUserExpansion();
+        }
+
+        public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+            if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+        }
+
+        public void setAmbientPulsing(boolean pulsing) {
+            if (row != null) row.setAmbientPulsing(pulsing);
+        }
+
+        public boolean rowExists() {
+            return row != null;
+        }
+
+        public boolean isRowDismissed() {
+            return row != null && row.isDismissed();
+        }
+
+        public boolean isRowRemoved() {
+            return row != null && row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or removed
+         */
+        public boolean isRemoved() {
+            //TODO: recycling invalidates this
+            return row == null || row.isRemoved();
+        }
+
+        /**
+         * @return {@code true} if the row is null or dismissed
+         */
+        public boolean isDismissed() {
+            //TODO: recycling
+            return row == null || row.isDismissed();
+        }
+
+        public boolean isRowPinned() {
+            return row != null && row.isPinned();
+        }
+
+        public void setRowPinned(boolean pinned) {
+            if (row != null) row.setPinned(pinned);
+        }
+
+        public boolean isRowAnimatingAway() {
+            return row != null && row.isHeadsUpAnimatingAway();
+        }
+
+        public boolean isRowHeadsUp() {
+            return row != null && row.isHeadsUp();
+        }
+
+        public void setHeadsUp(boolean shouldHeadsUp) {
+            if (row != null) row.setHeadsUp(shouldHeadsUp);
+        }
+
+        public boolean mustStayOnScreen() {
+            return row != null && row.mustStayOnScreen();
+        }
+
+        public void setHeadsUpIsVisible() {
+            if (row != null) row.setHeadsUpIsVisible();
+        }
+
+        //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+        public ExpandableNotificationRow getHeadsUpAnimationView() {
+            return row;
+        }
+
+        public void setUserLocked(boolean userLocked) {
+            if (row != null) row.setUserLocked(userLocked);
+        }
+
+        public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+            if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+        }
+
+        public void setGroupExpansionChanging(boolean changing) {
+            if (row != null) row.setGroupExpansionChanging(changing);
+        }
+
+        public void notifyHeightChanged(boolean needsAnimation) {
+            if (row != null) row.notifyHeightChanged(needsAnimation);
+        }
+
+        public void closeRemoteInput() {
+            if (row != null) row.closeRemoteInput();
+        }
+
+        public boolean areChildrenExpanded() {
+            return row != null && row.areChildrenExpanded();
+        }
+
+        public boolean keepInParent() {
+            return row != null && row.keepInParent();
+        }
+
+        //TODO: probably less confusing to say "is group fully visible"
+        public boolean isGroupNotFullyVisible() {
+            return row == null || row.isGroupNotFullyVisible();
+        }
+
+        public NotificationGuts getGuts() {
+            if (row != null) return row.getGuts();
+            return null;
+        }
+
+        public boolean hasLowPriorityStateUpdated() {
+            return row != null && row.hasLowPriorityStateUpdated();
+        }
+
+        public void removeRow() {
+            if (row != null) row.setRemoved();
+        }
+
+        public boolean isSummaryWithChildren() {
+            return row != null && row.isSummaryWithChildren();
+        }
+
+        public void setKeepInParent(boolean keep) {
+            if (row != null) row.setKeepInParent(keep);
+        }
+
+        public void onDensityOrFontScaleChanged() {
+            if (row != null) row.onDensityOrFontScaleChanged();
+        }
+
+        public boolean areGutsExposed() {
+            return row != null && row.getGuts().isExposed();
+        }
+
+        public boolean isChildInGroup() {
+            return parent == null;
+        }
+
+        public void setLowPriorityStateUpdated(boolean updated) {
+            if (row != null) row.setLowPriorityStateUpdated(updated);
+        }
+
+        /**
+         * @return Can the underlying notification be cleared? This can be different from whether the
+         *         notification can be dismissed in case notifications are sensitive on the lockscreen.
+         * @see #canViewBeDismissed()
+         */
+        public boolean isClearable() {
+            if (notification == null || !notification.isClearable()) {
+                return false;
+            }
+            if (children.size() > 0) {
+                for (int i = 0; i < children.size(); i++) {
+                    Entry child =  children.get(i);
+                    if (!child.isClearable()) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        public boolean canViewBeDismissed() {
+            if (row == null) return true;
+            return row.canViewBeDismissed();
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 450d34d..70d144e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,7 +24,6 @@
 
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -177,9 +176,12 @@
             }
 
             // Check if the notification is displaying the menu, if so slide notification back
-            if (row.getProvider() != null && row.getProvider().isMenuVisible()) {
+            if (isMenuVisible(row)) {
                 row.animateTranslateNotification(0);
                 return;
+            } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
+                row.getNotificationParent().animateTranslateNotification(0);
+                return;
             }
 
             // Mark notification for one frame.
@@ -194,6 +196,10 @@
             mCallback.onNotificationClicked(sbn, row);
         }
 
+        private boolean isMenuVisible(ExpandableNotificationRow row) {
+            return row.getProvider() != null && row.getProvider().isMenuVisible();
+        }
+
         public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
             Notification notification = sbn.getNotification();
             if (notification.contentIntent != null || notification.fullScreenIntent != null) {
@@ -385,9 +391,9 @@
                 entry.notification.getUser().getIdentifier());
 
         final StatusBarNotification sbn = entry.notification;
-        if (entry.row != null) {
+        if (entry.rowExists()) {
             entry.reset();
-            updateNotification(entry, pmUser, sbn, entry.row);
+            updateNotification(entry, pmUser, sbn, entry.getRow());
         } else {
             new RowInflaterTask().inflate(mContext, parent, entry,
                     row -> {
@@ -543,14 +549,14 @@
                 // Mark as seen immediately
                 setNotificationShown(entry.notification);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
             }
         }
         if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             if (shouldPulse(entry)) {
                 mAmbientPulseManager.showNotification(entry);
             } else {
-                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+                entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
             }
         }
     }
@@ -561,20 +567,20 @@
         mPendingNotifications.remove(entry.key);
         // If there was an async task started after the removal, we don't want to add it back to
         // the list, otherwise we might get leaks.
-        if (!entry.row.isRemoved()) {
+        if (!entry.isRowRemoved()) {
             boolean isNew = mNotificationData.get(entry.key) == null;
             if (isNew) {
                 showAlertingView(entry, inflatedFlags);
                 addEntry(entry);
             } else {
-                if (entry.row.hasLowPriorityStateUpdated()) {
+                if (entry.getRow().hasLowPriorityStateUpdated()) {
                     mVisualStabilityManager.onLowPriorityUpdated(entry);
                     mPresenter.updateNotificationViews();
                 }
                 mGroupAlertTransferHelper.onInflationFinished(entry);
             }
         }
-        entry.row.setLowPriorityStateUpdated(false);
+        entry.setLowPriorityStateUpdated(false);
     }
 
     @Override
@@ -633,9 +639,9 @@
         getMediaManager().onNotificationRemoved(key);
         mForegroundServiceController.removeNotification(entry.notification);
 
-        if (entry.row != null) {
-            entry.row.setRemoved();
-            mListContainer.cleanUpViewState(entry.row);
+        if (entry.rowExists()) {
+            entry.removeRow();
+            mListContainer.cleanUpViewStateForEntry(entry);
         }
 
         // Let's remove the children if this was a summary
@@ -670,19 +676,19 @@
      */
     private void handleGroupSummaryRemoved(String key) {
         NotificationData.Entry entry = mNotificationData.get(key);
-        if (entry != null && entry.row != null
-                && entry.row.isSummaryWithChildren()) {
-            if (entry.notification.getOverrideGroupKey() != null && !entry.row.isDismissed()) {
+        if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
+            if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
                 // We don't want to remove children for autobundled notifications as they are not
                 // always cancelled. We only remove them if they were dismissed by the user.
                 return;
             }
-            List<ExpandableNotificationRow> notificationChildren =
-                    entry.row.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow row = notificationChildren.get(i);
-                NotificationData.Entry childEntry = row.getEntry();
-                boolean isForeground = (row.getStatusBarNotification().getNotification().flags
+            List<NotificationData.Entry> childEntries = entry.getChildren();
+            if (childEntries == null) {
+                return;
+            }
+            for (int i = 0; i < childEntries.size(); i++) {
+                NotificationData.Entry childEntry = childEntries.get(i);
+                boolean isForeground = (entry.notification.getNotification().flags
                         & Notification.FLAG_FOREGROUND_SERVICE) != 0;
                 boolean keepForReply =
                         getRemoteInputManager().shouldKeepForRemoteInputHistory(childEntry)
@@ -692,10 +698,10 @@
                     // a child we're keeping around for reply!
                     continue;
                 }
-                row.setKeepInParent(true);
+                entry.setKeepInParent(true);
                 // we need to set this state earlier as otherwise we might generate some weird
                 // animations
-                row.setRemoved();
+                entry.removeRow();
             }
         }
     }
@@ -705,15 +711,15 @@
                 mNotificationData.getNotificationsForCurrentUser();
         for (int i = 0; i < userNotifications.size(); i++) {
             NotificationData.Entry entry = userNotifications.get(i);
-            boolean exposedGuts = mGutsManager.getExposedGuts() != null
-                    && entry.row.getGuts() == mGutsManager.getExposedGuts();
-            entry.row.onDensityOrFontScaleChanged();
+            entry.onDensityOrFontScaleChanged();
+            boolean exposedGuts = entry.areGutsExposed();
             if (exposedGuts) {
-                mGutsManager.onDensityOrFontScaleChanged(entry.row);
+                mGutsManager.onDensityOrFontScaleChanged(entry);
             }
         }
     }
 
+    //TODO: This method associates a row with an entry, but eventually needs to not do that
     protected void updateNotification(NotificationData.Entry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
@@ -736,8 +742,8 @@
         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(mPresenter);
+        entry.setRow(row);
+        row.setOnActivatedListener(mPresenter);
 
         boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
                 mNotificationData.getImportance(sbn.getKey()));
@@ -766,7 +772,7 @@
         }
 
         NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking);
-        if (shouldAutoBubble(entry)) {
+        if (BubbleController.shouldAutoBubble(getContext(), entry)) {
             entry.setIsBubble(true);
         }
 
@@ -910,7 +916,7 @@
         if (!notification.isClearable()) {
             // The user may have performed a dismiss action on the notification, since it's
             // not clearable we should snap it back.
-            mListContainer.snapViewIfNeeded(entry.row);
+            mListContainer.snapViewIfNeeded(entry);
         }
 
         if (DEBUG) {
@@ -946,10 +952,12 @@
 
         // Has a copy of the current UI adjustments.
         ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
+        ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
         for (NotificationData.Entry entry : entries) {
             NotificationUiAdjustment adjustment =
                     NotificationUiAdjustment.extractFromNotificationEntry(entry);
             oldAdjustments.put(entry.key, adjustment);
+            oldImportances.put(entry.key, entry.importance);
         }
 
         // Populate notification entries from the new rankings.
@@ -963,15 +971,20 @@
 
             if (NotificationUiAdjustment.needReinflate(
                     oldAdjustments.get(entry.key), newAdjustment)) {
-                if (entry.row != null) {
+                if (entry.rowExists()) {
                     entry.reset();
                     PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
                             entry.notification.getUser().getIdentifier());
-                    updateNotification(entry, pmUser, entry.notification, entry.row);
+                    updateNotification(entry, pmUser, entry.notification, entry.getRow());
                 } else {
                     // Once the RowInflaterTask is done, it will pick up the updated entry, so
                     // no-op here.
                 }
+            } else if (oldImportances.containsKey(entry.key)
+                    && entry.importance != oldImportances.get(entry.key)) {
+                if (entry.rowExists()) {
+                    entry.getRow().onNotificationRankingUpdated();
+                }
             }
         }
 
@@ -1207,17 +1220,6 @@
         }
     }
 
-
-    /**
-     * Whether a bubble is appropriate to auto-bubble or not.
-     */
-    private boolean shouldAutoBubble(NotificationData.Entry entry) {
-        int priority = mNotificationData.getImportance(entry.key);
-        NotificationChannel channel = mNotificationData.getChannel(entry.key);
-        boolean canAppOverlay = channel != null && channel.canOverlayApps();
-        return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay);
-    }
-
     /**
      * Callback for NotificationEntryManager.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 1f48c15..09eb8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -75,6 +75,6 @@
     /** Returns the value of the new interruption model setting. */
     public static boolean useNewInterruptionModel(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
-                NOTIFICATION_NEW_INTERRUPTION_MODEL, 0) != 0;
+                NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) != 0;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index 53ebe74..a194eef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,18 +16,16 @@
 
 package com.android.systemui.statusbar.notification;
 
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-
 /**
  * An object that can determine the visibility of a Notification.
  */
 public interface VisibilityLocationProvider {
 
     /**
-     * Returns whether an ExpandableNotificationRow is in a visible location or not.
+     * Returns whether an Entry is in a visible location or not.
      *
-     * @param row
+     * @param entry
      * @return true if row is in a visible location
      */
-    boolean isInVisibleLocation(ExpandableNotificationRow row);
+    boolean isInVisibleLocation(NotificationData.Entry entry);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 75613a4..fce7980 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -120,7 +120,7 @@
             return true;
         }
         if (mAllowedReorderViews.contains(row)
-                && !mVisibilityLocationProvider.isInVisibleLocation(row)) {
+                && !mVisibilityLocationProvider.isInVisibleLocation(row.getEntry())) {
             return true;
         }
         return false;
@@ -142,12 +142,12 @@
         if (isHeadsUp) {
             // Heads up notifications should in general be allowed to reorder if they are out of
             // view and stay at the current location if they aren't.
-            mAllowedReorderViews.add(entry.row);
+            mAllowedReorderViews.add(entry.getRow());
         }
     }
 
     public void onLowPriorityUpdated(NotificationData.Entry entry) {
-        mLowPriorityReorderingViews.add(entry.row);
+        mLowPriorityReorderingViews.add(entry.getRow());
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 5dfd5d0..9f02e54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -115,7 +115,7 @@
             for (int i = 0; i < N; i++) {
                 NotificationData.Entry entry = activeNotifications.get(i);
                 String key = entry.notification.getKey();
-                boolean isVisible = mListContainer.isInVisibleLocation(entry.row);
+                boolean isVisible = mListContainer.isInVisibleLocation(entry);
                 NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
                 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(visObj);
                 if (isVisible) {
@@ -149,7 +149,7 @@
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         // Not expected to be destroyed, don't need to unsubscribe
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     public void setUpWithContainer(NotificationListContainer listContainer) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index daec9c9..69e698f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,22 +17,14 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
-        .ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentView
-        .VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .InflationCallback;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -104,7 +96,7 @@
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -336,7 +328,6 @@
     private float mTranslationWhenRemoved;
     private boolean mWasChildInGroupWhenRemoved;
     private int mNotificationColorAmbient;
-    private NotificationViewState mNotificationViewState;
 
     private SystemNotificationAsyncTask mSystemNotificationAsyncTask =
             new SystemNotificationAsyncTask();
@@ -597,6 +588,13 @@
         updateRippleAllowed();
     }
 
+    /** Called when the notification's ranking was changed (but nothing else changed). */
+    public void onNotificationRankingUpdated() {
+        if (mMenuRow != null) {
+            mMenuRow.onNotificationUpdated(mStatusBarNotification);
+        }
+    }
+
     @VisibleForTesting
     void updateShelfIconColor() {
         StatusBarIconView expandedIcon = mEntry.expandedIcon;
@@ -893,29 +891,32 @@
                 visualStabilityManager, callback);
     }
 
-    public void getChildrenStates(StackScrollState resultState,
-            AmbientState ambientState) {
+    /** Updates states of all children. */
+    public void updateChildrenStates(AmbientState ambientState) {
         if (mIsSummaryWithChildren) {
-            ExpandableViewState parentState = resultState.getViewStateForView(this);
-            mChildrenContainer.getState(resultState, parentState, ambientState);
+            ExpandableViewState parentState = getViewState();
+            mChildrenContainer.updateState(parentState, ambientState);
         }
     }
 
-    public void applyChildrenState(StackScrollState state) {
+    /** Applies children states. */
+    public void applyChildrenState() {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.applyState(state);
+            mChildrenContainer.applyState();
         }
     }
 
-    public void prepareExpansionChanged(StackScrollState state) {
+    /** Prepares expansion changed. */
+    public void prepareExpansionChanged() {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.prepareExpansionChanged(state);
+            mChildrenContainer.prepareExpansionChanged();
         }
     }
 
-    public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
+    /** Starts child animations. */
+    public void startChildAnimation(AnimationProperties properties) {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.startAnimationToState(finalState, properties);
+            mChildrenContainer.startAnimationToState(properties);
         }
     }
 
@@ -1411,16 +1412,16 @@
 
     public void performDismiss(boolean fromAccessibility) {
         if (isOnlyChildInGroup()) {
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
                 // If this is the only child in the group, dismiss the group, but don't try to show
                 // the blocking helper affordance!
-                groupSummary.performDismiss(fromAccessibility);
+                groupSummary.getRow().performDismiss(fromAccessibility);
             }
         }
         setDismissed(fromAccessibility);
-        if (isClearable()) {
+        if (mEntry.isClearable()) {
             // TODO: track dismiss sentiment
             if (mOnDismissRunnable != null) {
                 mOnDismissRunnable.run();
@@ -2007,7 +2008,8 @@
         float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
         int startClipTopAmount = params.getStartClipTopAmount();
         if (mNotificationParent != null) {
-            top -= mNotificationParent.getTranslationY();
+            float parentY = mNotificationParent.getTranslationY();
+            top -= parentY;
             mNotificationParent.setTranslationZ(translationZ);
             int parentStartClipTopAmount = params.getParentStartClipTopAmount();
             if (startClipTopAmount != 0) {
@@ -2017,8 +2019,12 @@
                 mNotificationParent.setClipTopAmount(clipTopAmount);
             }
             mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
-            mNotificationParent.setMinimumHeightForClipping(params.getHeight()
-                    + mNotificationParent.getActualHeight());
+            float clipBottom = Math.max(params.getBottom(),
+                    parentY + mNotificationParent.getActualHeight()
+                            - mNotificationParent.getClipBottomAmount());
+            float clipTop = Math.min(params.getTop(), parentY);
+            int minimumHeightForClipping = (int) (clipBottom - clipTop);
+            mNotificationParent.setMinimumHeightForClipping(minimumHeightForClipping);
         } else if (startClipTopAmount != 0) {
             int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
             setClipTopAmount(clipTopAmount);
@@ -2046,7 +2052,7 @@
                     .setInterpolator(Interpolators.ALPHA_OUT);
             setAboveShelf(true);
             mExpandAnimationRunning = true;
-            mNotificationViewState.cancelAnimations(this);
+            getViewState().cancelAnimations(this);
             mNotificationLaunchHeight = AmbientState.getNotificationLaunchHeight(getContext());
         } else {
             mExpandAnimationRunning = false;
@@ -2073,6 +2079,8 @@
 
     private void setChildIsExpanding(boolean isExpanding) {
         mChildIsExpanding = isExpanding;
+        updateClipping();
+        invalidate();
     }
 
     @Override
@@ -2244,28 +2252,6 @@
         setRippleAllowed(allowed);
     }
 
-    /**
-     * @return Can the underlying notification be cleared? This can be different from whether the
-     *         notification can be dismissed in case notifications are sensitive on the lockscreen.
-     * @see #canViewBeDismissed()
-     */
-    public boolean isClearable() {
-        if (mStatusBarNotification == null || !mStatusBarNotification.isClearable()) {
-            return false;
-        }
-        if (mIsSummaryWithChildren) {
-            List<ExpandableNotificationRow> notificationChildren =
-                    mChildrenContainer.getNotificationChildren();
-            for (int i = 0; i < notificationChildren.size(); i++) {
-                ExpandableNotificationRow child = notificationChildren.get(i);
-                if (!child.isClearable()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
     @Override
     public int getIntrinsicHeight() {
         if (isShownAsBubble()) {
@@ -2533,10 +2519,10 @@
     /**
      * @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
      *         otherwise some state might not be updated. To request about the general clearability
-     *         see {@link #isClearable()}.
+     *         see {@link NotificationData.Entry#isClearable()}.
      */
     public boolean canViewBeDismissed() {
-        return isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+        return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
     }
 
     private boolean shouldShowPublic() {
@@ -2945,13 +2931,8 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
-        mNotificationViewState = new NotificationViewState(stackScrollState);
-        return mNotificationViewState;
-    }
-
-    public NotificationViewState getViewState() {
-        return mNotificationViewState;
+    public ExpandableViewState createExpandableViewState() {
+        return new NotificationViewState();
     }
 
     @Override
@@ -3001,7 +2982,7 @@
                 return true;
             }
         } else if (child == mChildrenContainer) {
-            if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
+            if (isClippingNeeded() || !hasNoRounding()) {
                 return true;
             }
         } else if (child instanceof NotificationGuts) {
@@ -3038,6 +3019,21 @@
         return mOnAmbient;
     }
 
+    //TODO: this logic can't depend on layout if we are recycling!
+    public boolean isMediaRow() {
+        return getExpandedContentView() != null
+                && getExpandedContentView().findViewById(
+                com.android.internal.R.id.media_actions) != null;
+    }
+
+    public boolean isTopLevelChild() {
+        return getParent() instanceof NotificationStackScrollLayout;
+    }
+
+    public boolean isGroupNotFullyVisible() {
+        return getClipTopAmount() > 0 || getTranslationY() < 0;
+    }
+
     public void setAboveShelf(boolean aboveShelf) {
         boolean wasAboveShelf = isAboveShelf();
         mAboveShelf = aboveShelf;
@@ -3046,14 +3042,7 @@
         }
     }
 
-    public static class NotificationViewState extends ExpandableViewState {
-
-        private final StackScrollState mOverallState;
-
-
-        private NotificationViewState(StackScrollState stackScrollState) {
-            mOverallState = stackScrollState;
-        }
+    private static class NotificationViewState extends ExpandableViewState {
 
         @Override
         public void applyToView(View view) {
@@ -3064,7 +3053,7 @@
                 }
                 handleFixedTranslationZ(row);
                 super.applyToView(view);
-                row.applyChildrenState(mOverallState);
+                row.applyChildrenState();
             }
         }
 
@@ -3095,7 +3084,7 @@
                 }
                 handleFixedTranslationZ(row);
                 super.animateTo(child, properties);
-                row.startChildAnimation(mOverallState, properties);
+                row.startChildAnimation(properties);
             }
         }
     }
@@ -3150,8 +3139,8 @@
         pw.println();
         showingLayout.dump(fd, pw, args);
         pw.print("    ");
-        if (mNotificationViewState != null) {
-            mNotificationViewState.dump(fd, pw, args);
+        if (getViewState() != null) {
+            getViewState().dump(fd, pw, args);
         } else {
             pw.print("no viewState!!!");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index a7aed5f..0efb130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -115,12 +115,14 @@
         if (!mCustomOutline) {
             int translation = mShouldTranslateContents && !ignoreTranslation
                     ? (int) getTranslation() : 0;
-            left = Math.max(translation, 0);
+            int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
+            left = Math.max(translation, 0) - halfExtraWidth;
             top = mClipTopAmount + mBackgroundTop;
-            right = getWidth() + Math.min(translation, 0);
+            right = getWidth() + halfExtraWidth + Math.min(translation, 0);
             // If the top is rounded we want the bottom to be at most at the top roundness, in order
             // to avoid the shadow changing when scrolling up.
-            bottom = Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness));
+            bottom = Math.max(mMinimumHeightForClipping,
+                    Math.max(getActualHeight() - mClipBottomAmount, (int) (top + topRoundness)));
         } else {
             left = mOutlineRect.left;
             top = mOutlineRect.top;
@@ -219,10 +221,12 @@
 
     public void setExtraWidthForClipping(float extraWidthForClipping) {
         mExtraWidthForClipping = extraWidthForClipping;
+        invalidate();
     }
 
     public void setMinimumHeightForClipping(int minimumHeightForClipping) {
         mMinimumHeightForClipping = minimumHeightForClipping;
+        invalidate();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 0589e3f..1e8de07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -21,23 +21,27 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * An abstract view for expandable views.
  */
 public abstract class ExpandableView extends FrameLayout implements Dumpable {
+    private static final String TAG = "ExpandableView";
 
     public static final float NO_ROUNDNESS = -1;
     protected OnHeightChangedListener mOnHeightChangedListener;
@@ -54,6 +58,7 @@
     private ViewGroup mTransientContainer;
     private boolean mInShelf;
     private boolean mTransformingInShelf;
+    @Nullable private ExpandableViewState mViewState;
 
     public ExpandableView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -511,10 +516,56 @@
 
     public void setActualHeightAnimating(boolean animating) {}
 
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    protected ExpandableViewState createExpandableViewState() {
         return new ExpandableViewState();
     }
 
+    /** Sets {@link ExpandableViewState} to default state. */
+    public ExpandableViewState resetViewState() {
+        // TODO(http://b/119762423): Move the null check to getViewState().
+        if (mViewState == null) {
+            mViewState = createExpandableViewState();
+        }
+
+        // initialize with the default values of the view
+        mViewState.height = getIntrinsicHeight();
+        mViewState.gone = getVisibility() == View.GONE;
+        mViewState.alpha = 1f;
+        mViewState.notGoneIndex = -1;
+        mViewState.xTranslation = getTranslationX();
+        mViewState.hidden = false;
+        mViewState.scaleX = getScaleX();
+        mViewState.scaleY = getScaleY();
+        mViewState.inShelf = false;
+        mViewState.headsUpIsVisible = false;
+
+        // handling reset for child notifications
+        if (this instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) this;
+            List<ExpandableNotificationRow> children = row.getNotificationChildren();
+            if (row.isSummaryWithChildren() && children != null) {
+                for (ExpandableNotificationRow childRow : children) {
+                    childRow.resetViewState();
+                }
+            }
+        }
+
+        return mViewState;
+    }
+
+    @Nullable public ExpandableViewState getViewState() {
+        return mViewState;
+    }
+
+    /** Applies internal {@link ExpandableViewState} to this view. */
+    public void applyViewState() {
+        if (mViewState == null) {
+            Log.wtf(TAG, "No child state was found when applying this state to the hostView");
+        } else if (!mViewState.gone) {
+            mViewState.applyToView(this);
+        }
+    }
+
     /**
      * @return whether the current view doesn't add height to the overall content. This means that
      * if it is added to a list of items, it's content will still have the same height.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 1f15ed0..311bf7a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -24,7 +24,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
-import com.android.systemui.statusbar.notification.stack.StackScrollState;
 
 public class FooterView extends StackScrollerDecorView {
     private final int mClearAllTopPadding;
@@ -87,7 +86,7 @@
     }
 
     @Override
-    public ExpandableViewState createNewViewState(StackScrollState stackScrollState) {
+    public ExpandableViewState createExpandableViewState() {
         return new FooterViewState();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index aa5ab71..689d6d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -57,7 +57,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -493,12 +492,12 @@
         if (child == null) {
             mExpandedChild = null;
             mExpandedWrapper = null;
-            if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
-                mVisibleType = VISIBLE_TYPE_CONTRACTED;
-            }
             if (mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED) {
                 mTransformationStartVisibleType = UNDEFINED;
             }
+            if (mVisibleType == VISIBLE_TYPE_EXPANDED) {
+                selectLayout(false /* animate */, true /* force */);
+            }
             return;
         }
         addView(child);
@@ -531,12 +530,12 @@
         if (child == null) {
             mHeadsUpChild = null;
             mHeadsUpWrapper = null;
-            if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
-                mVisibleType = VISIBLE_TYPE_CONTRACTED;
-            }
             if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
                 mTransformationStartVisibleType = UNDEFINED;
             }
+            if (mVisibleType == VISIBLE_TYPE_HEADSUP) {
+                selectLayout(false /* animate */, true /* force */);
+            }
             return;
         }
         addView(child);
@@ -558,12 +557,12 @@
         if (child == null) {
             mAmbientChild = null;
             mAmbientWrapper = null;
-            if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
-                mVisibleType = VISIBLE_TYPE_CONTRACTED;
-            }
             if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
                 mTransformationStartVisibleType = UNDEFINED;
             }
+            if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+                selectLayout(false /* animate */, true /* force */);
+            }
             return;
         }
         addView(child);
@@ -1230,17 +1229,18 @@
         mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
+        ExpandableNotificationRow row = entry.getRow();
         if (mContractedChild != null) {
-            mContractedWrapper.onContentUpdated(entry.row);
+            mContractedWrapper.onContentUpdated(row);
         }
         if (mExpandedChild != null) {
-            mExpandedWrapper.onContentUpdated(entry.row);
+            mExpandedWrapper.onContentUpdated(row);
         }
         if (mHeadsUpChild != null) {
-            mHeadsUpWrapper.onContentUpdated(entry.row);
+            mHeadsUpWrapper.onContentUpdated(row);
         }
         if (mAmbientChild != null) {
-            mAmbientWrapper.onContentUpdated(entry.row);
+            mAmbientWrapper.onContentUpdated(row);
         }
         applyRemoteInputAndSmartReply(entry);
         updateLegacy();
@@ -1519,7 +1519,8 @@
                         smartRepliesAndActions.smartReplies, mSmartReplyController, entry);
             }
             if (smartRepliesAndActions.smartActions != null) {
-                smartReplyView.addSmartActions(smartRepliesAndActions.smartActions);
+                smartReplyView.addSmartActions(
+                        smartRepliesAndActions.smartActions, mSmartReplyController, entry);
             }
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 37bf06e..3dc50ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -18,10 +18,7 @@
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
-
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
@@ -43,7 +40,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.plugins.ActivityStarter;
@@ -86,7 +82,6 @@
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
 
     // which notification is currently being longpress-examined by the user
-    private final IStatusBarService mBarService;
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
     private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
@@ -103,8 +98,6 @@
 
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        mBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter,
@@ -116,9 +109,9 @@
         mOnSettingsClickListener = onSettingsClick;
     }
 
-    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
-        setExposedGuts(row.getGuts());
-        bindGuts(row);
+    public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+        setExposedGuts(entry.getGuts());
+        bindGuts(entry.getRow());
     }
 
     /**
@@ -191,13 +184,7 @@
             } else if (gutsView instanceof AppOpsInfo) {
                 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
             } else if (gutsView instanceof NotificationInfo) {
-                int action;
-                if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) {
-                    action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction;
-                } else {
-                    action = ACTION_NONE;
-                }
-                initializeNotificationInfo(row, (NotificationInfo) gutsView, action);
+                initializeNotificationInfo(row, (NotificationInfo) gutsView);
             }
             return true;
         } catch (Exception e) {
@@ -256,13 +243,11 @@
      * Sets up the {@link NotificationInfo} inside the notification row's guts.
      * @param row view to set up the guts for
      * @param notificationInfoView view to set up/bind within {@code row}
-     * @param action The action to take immediately upon binding, if any.
      */
     @VisibleForTesting
     void initializeNotificationInfo(
             final ExpandableNotificationRow row,
-            NotificationInfo notificationInfoView,
-            @NotificationInfo.NotificationInfoAction int action) throws Exception {
+            NotificationInfo notificationInfoView) throws Exception {
         NotificationGuts guts = row.getGuts();
         StatusBarNotification sbn = row.getStatusBarNotification();
         String packageName = sbn.getPackageName();
@@ -305,9 +290,7 @@
                 row.getIsNonblockable(),
                 isForBlockingHelper,
                 row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
-                row.getEntry().noisy,
-                row.getEntry().importance,
-                action);
+                row.getEntry().importance);
 
     }
 
@@ -441,8 +424,8 @@
     public boolean shouldExtendLifetime(NotificationData.Entry entry) {
         return entry != null
                 &&(mNotificationGutsExposed != null
-                    && entry.row.getGuts() != null
-                    && mNotificationGutsExposed == entry.row.getGuts()
+                    && entry.getGuts() != null
+                    && mNotificationGutsExposed == entry.getGuts()
                     && !mNotificationGutsExposed.isLeavebehind());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 70860258..ef343fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -697,7 +697,7 @@
                         && newView.getPackage() != null
                         && newView.getPackage().equals(oldView.getPackage())
                         && newView.getLayoutId() == oldView.getLayoutId()
-                        && !oldView.isReapplyDisallowed());
+                        && !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED));
     }
 
     public void setInflationCallback(InflationCallback callback) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 0d36d2c..5329541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -104,7 +104,6 @@
     private AnimatorSet mExpandAnimation;
     private boolean mIsForeground;
     private boolean mIsDeviceProvisioned;
-    private boolean mIsNoisy;
 
     private CheckSaveListener mCheckSaveListener;
     private OnSettingsClickListener mOnSettingsClickListener;
@@ -186,15 +185,13 @@
             final OnAppSettingsClickListener onAppSettingsClick,
             boolean isDeviceProvisioned,
             boolean isNonblockable,
-            boolean isNoisy,
-            int importance,
-            @NotificationInfoAction int action)
+            int importance)
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel,
                 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
                 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
-                false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy,
-                importance, action);
+                false /* isBlockingHelper */, false /* isUserSentimentNegative */,
+                importance);
     }
 
     public void bindNotification(
@@ -211,9 +208,7 @@
             boolean isNonblockable,
             boolean isForBlockingHelper,
             boolean isUserSentimentNegative,
-            boolean isNoisy,
-            int importance,
-            @NotificationInfoAction int action)
+            int importance)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
         mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -238,7 +233,6 @@
         mAppUid = mSbn.getUid();
         mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
-        mIsNoisy = isNoisy;
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
@@ -255,10 +249,6 @@
         bindHeader();
         bindPrompt();
         bindButtons();
-
-        if (action != ACTION_NONE) {
-            swapContent(action, false /* don't animate */);
-        }
     }
 
     private void bindHeader() throws RemoteException {
@@ -417,54 +407,74 @@
     }
 
     private void bindButtons() {
-        // Set up stay-in-notification actions
-        View block =  findViewById(R.id.block);
-        TextView keep = findViewById(R.id.keep);
-        TextView silent = findViewById(R.id.toggle_silent);
-        View minimize = findViewById(R.id.minimize);
-
         findViewById(R.id.undo).setOnClickListener(mOnUndo);
-        block.setOnClickListener(mOnStopOrMinimizeNotifications);
-        keep.setOnClickListener(mOnKeepShowing);
-        silent.setOnClickListener(mOnToggleSilent);
-        minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
 
-        if (mIsNonblockable) {
-            keep.setText(android.R.string.ok);
-            block.setVisibility(GONE);
-            silent.setVisibility(GONE);
-            minimize.setVisibility(GONE);
-        } else if (mIsForeground) {
-            block.setVisibility(GONE);
-            silent.setVisibility(GONE);
-            minimize.setVisibility(VISIBLE);
-        } else {
-            block.setVisibility(VISIBLE);
-            boolean showToggleSilent = mIsNoisy
-                    && NotificationUtils.useNewInterruptionModel(mContext);
-            silent.setVisibility(showToggleSilent ? VISIBLE : GONE);
+        boolean showInterruptivenessSettings =
+                !mIsNonblockable
+                        && !mIsForeground
+                        && !mIsForBlockingHelper
+                        && NotificationUtils.useNewInterruptionModel(mContext);
+        if (showInterruptivenessSettings) {
+            findViewById(R.id.block_or_minimize).setVisibility(GONE);
+            findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
+            View block = findViewById(R.id.int_block);
+            TextView silent = findViewById(R.id.int_silent);
+            TextView alert = findViewById(R.id.int_alert);
+
             boolean isCurrentlyAlerting =
                     mStartingChannelOrNotificationImportance >= IMPORTANCE_DEFAULT;
-            silent.setText(isCurrentlyAlerting
-                    ? R.string.inline_silent_button_silent
-                    : R.string.inline_silent_button_alert);
-            minimize.setVisibility(GONE);
-        }
 
-        // Set up app settings link (i.e. Customize)
-        TextView settingsLinkView = findViewById(R.id.app_settings);
-        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
-                mSbn.getId(), mSbn.getTag());
-        if (!mIsForBlockingHelper
-                && settingsIntent != null
-                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
-            settingsLinkView.setVisibility(VISIBLE);
-            settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
-            settingsLinkView.setOnClickListener((View view) -> {
-                mAppSettingsClickListener.onClick(view, settingsIntent);
-            });
+            block.setOnClickListener(mOnStopOrMinimizeNotifications);
+            if (isCurrentlyAlerting) {
+                silent.setOnClickListener(mOnToggleSilent);
+                silent.setText(R.string.inline_silent_button_silent);
+                alert.setOnClickListener(mOnKeepShowing);
+                alert.setText(R.string.inline_silent_button_keep_alerting);
+            } else {
+                silent.setOnClickListener(mOnKeepShowing);
+                silent.setText(R.string.inline_silent_button_stay_silent);
+                alert.setOnClickListener(mOnToggleSilent);
+                alert.setText(R.string.inline_silent_button_alert);
+            }
         } else {
-            settingsLinkView.setVisibility(View.GONE);
+            findViewById(R.id.block_or_minimize).setVisibility(VISIBLE);
+            findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
+            View block = findViewById(R.id.block);
+            TextView keep = findViewById(R.id.keep);
+            View minimize = findViewById(R.id.minimize);
+
+            block.setOnClickListener(mOnStopOrMinimizeNotifications);
+            keep.setOnClickListener(mOnKeepShowing);
+            minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
+
+            if (mIsNonblockable) {
+                keep.setText(android.R.string.ok);
+                block.setVisibility(GONE);
+                minimize.setVisibility(GONE);
+            } else if (mIsForeground) {
+                block.setVisibility(GONE);
+                minimize.setVisibility(VISIBLE);
+            } else {
+                block.setVisibility(VISIBLE);
+                minimize.setVisibility(GONE);
+            }
+
+            // Set up app settings link (i.e. Customize)
+            TextView settingsLinkView = findViewById(R.id.app_settings);
+            Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
+                    mSingleNotificationChannel,
+                    mSbn.getId(), mSbn.getTag());
+            if (!mIsForBlockingHelper
+                    && settingsIntent != null
+                    && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
+                settingsLinkView.setVisibility(VISIBLE);
+                settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
+                settingsLinkView.setOnClickListener((View view) -> {
+                    mAppSettingsClickListener.onClick(view, settingsIntent);
+                });
+            } else {
+                settingsLinkView.setVisibility(View.GONE);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b6ff6fc..50564e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -17,9 +17,6 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE;
-import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -46,7 +43,6 @@
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
-import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
@@ -73,7 +69,7 @@
 
     private Context mContext;
     private FrameLayout mMenuContainer;
-    private NotificationInfoMenuItem mInfoItem;
+    private NotificationMenuItem mInfoItem;
     private MenuItem mAppOpsItem;
     private MenuItem mSnoozeItem;
     private ArrayList<MenuItem> mLeftMenuItems;
@@ -248,36 +244,32 @@
         if (!isForeground) {
             // Only show snooze for non-foreground notifications
             mSnoozeItem = createSnoozeItem(mContext);
-            mLeftMenuItems.add(mSnoozeItem);
         }
-        mInfoItem = createInfoItem(mContext);
-        if (!NotificationUtils.useNewInterruptionModel(mContext)) {
-            mLeftMenuItems.add(mInfoItem);
-        }
-
         mAppOpsItem = createAppOpsItem(mContext);
-        mLeftMenuItems.add(mAppOpsItem);
-
         if (NotificationUtils.useNewInterruptionModel(mContext)) {
-            if (!mParent.getIsNonblockable()) {
-                mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView()));
-            }
-            // TODO(kprevas): this is duplicated logic
-            // but it's currently spread across NotificationGutsManager and NotificationInfo.
-            // Try to consolidate and reuse here.
-            boolean canToggleSilent = !mParent.getIsNonblockable()
-                    && !isForeground
-                    && mParent.getEntry().noisy;
-            if (canToggleSilent) {
-                int channelImportance = mParent.getEntry().channel.getImportance();
-                int effectiveImportance =
-                        channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
-                                ? mParent.getEntry().importance : channelImportance;
-                mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(),
-                        effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT));
-            }
+            int channelImportance = mParent.getEntry().channel.getImportance();
+            int effectiveImportance =
+                    channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED
+                            ? mParent.getEntry().importance : channelImportance;
+            mInfoItem = createInfoItem(mContext,
+                    effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT);
         } else {
-            mRightMenuItems.addAll(mLeftMenuItems);
+            mInfoItem = createInfoItem(mContext);
+        }
+
+        if (!NotificationUtils.useNewInterruptionModel(mContext)) {
+            if (!isForeground) {
+                mRightMenuItems.add(mSnoozeItem);
+            }
+            mRightMenuItems.add(mInfoItem);
+            mRightMenuItems.add(mAppOpsItem);
+            mLeftMenuItems.addAll(mRightMenuItems);
+        } else {
+            mRightMenuItems.add(mInfoItem);
+            mRightMenuItems.add(mAppOpsItem);
+            if (!isForeground) {
+                mRightMenuItems.add(mSnoozeItem);
+            }
         }
 
         populateMenuViews();
@@ -634,13 +626,24 @@
         return snooze;
     }
 
-    static NotificationInfoMenuItem createInfoItem(Context context) {
+    static NotificationMenuItem createInfoItem(Context context) {
         Resources res = context.getResources();
         String infoDescription = res.getString(R.string.notification_menu_gear_description);
         NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
                 R.layout.notification_info, null, false);
-        return new NotificationInfoMenuItem(context, infoDescription, infoContent,
-                R.drawable.ic_settings, ACTION_NONE);
+        return new NotificationMenuItem(context, infoDescription, infoContent,
+                R.drawable.ic_settings);
+    }
+
+    static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) {
+        Resources res = context.getResources();
+        String infoDescription = res.getString(R.string.notification_menu_gear_description);
+        NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
+                R.layout.notification_info, null, false);
+        int iconResId = isCurrentlySilent
+                ? R.drawable.ic_notifications_alert
+                : R.drawable.ic_notifications_silence;
+        return new NotificationMenuItem(context, infoDescription, infoContent, iconResId);
     }
 
     static MenuItem createAppOpsItem(Context context) {
@@ -651,29 +654,6 @@
         return info;
     }
 
-    private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) {
-        return new NotificationInfoMenuItem(
-                context,
-                context.getResources().getString(R.string.inline_stop_button),
-                gutsView,
-                R.drawable.ic_notification_block,
-                ACTION_BLOCK);
-    }
-
-    private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView,
-            boolean isCurrentlySilent) {
-        return new NotificationInfoMenuItem(
-                context,
-                isCurrentlySilent
-                        ? context.getResources().getString(R.string.inline_silent_button_alert)
-                        : context.getResources().getString(R.string.inline_silent_button_silent),
-                gutsView,
-                isCurrentlySilent
-                        ? R.drawable.ic_notifications_alert
-                        : R.drawable.ic_notifications_silence,
-                ACTION_TOGGLE_SILENT);
-    }
-
     private void addMenuView(MenuItem item, ViewGroup parent) {
         View menuView = item.getMenuView();
         if (menuView != null) {
@@ -789,23 +769,4 @@
             return mContentDescription;
         }
     }
-
-    /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */
-    public static class NotificationInfoMenuItem extends NotificationMenuItem {
-
-        @NotificationInfoAction
-        int mAction;
-
-        public NotificationInfoMenuItem(Context context, String contentDescription,
-                NotificationInfo content, int iconResId,
-                @NotificationInfoAction int action) {
-            super(context, contentDescription, content, iconResId);
-            this.mAction = action;
-        }
-
-        @Override
-        public NotificationInfo getGutsView() {
-            return (NotificationInfo) super.getGutsView();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ff1a6fc..670908f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -40,7 +40,7 @@
 
     private static final int NO_SECTION_BOUNDARY = -1;
 
-    private ArrayList<View> mDraggedViews = new ArrayList<>();
+    private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>();
     private int mScrollY;
     private boolean mDimmed;
     private ActivatableNotificationView mActivatedChild;
@@ -131,7 +131,8 @@
         this.mScrollY = scrollY;
     }
 
-    public void onBeginDrag(View view) {
+    /** Call when dragging begins. */
+    public void onBeginDrag(ExpandableView view) {
         mDraggedViews.add(view);
     }
 
@@ -139,7 +140,7 @@
         mDraggedViews.remove(view);
     }
 
-    public ArrayList<View> getDraggedViews() {
+    public ArrayList<ExpandableView> getDraggedViews() {
         return mDraggedViews;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 74b4aa2..0f38bd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -274,7 +274,7 @@
         updateGroupOverflow();
         row.setContentTransformationAmount(0, false /* isLastChild */);
         // It doesn't make sense to keep old animations around, lets cancel them!
-        ExpandableNotificationRow.NotificationViewState viewState = row.getViewState();
+        ExpandableViewState viewState = row.getViewState();
         if (viewState != null) {
             viewState.cancelAnimations(row);
             row.cancelAppearDrawing();
@@ -562,12 +562,10 @@
 
     /**
      * Update the state of all its children based on a linear layout algorithm.
-     *  @param resultState the state to update
      * @param parentState the state of the parent
      * @param ambientState
      */
-    public void getState(StackScrollState resultState, ExpandableViewState parentState,
-            AmbientState ambientState) {
+    public void updateState(ExpandableViewState parentState, AmbientState ambientState) {
         int childCount = mChildren.size();
         int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
@@ -605,7 +603,7 @@
                 firstChild = false;
             }
 
-            ExpandableViewState childState = resultState.getViewStateForView(child);
+            ExpandableViewState childState = child.getViewState();
             int intrinsicHeight = child.getIntrinsicHeight();
             childState.height = intrinsicHeight;
             childState.yTranslation = yPosition + launchTransitionCompensation;
@@ -639,7 +637,7 @@
         if (mOverflowNumber != null) {
             ExpandableNotificationRow overflowView = mChildren.get(Math.min(
                     getMaxAllowedVisibleChildren(true /* likeCollapsed */), childCount) - 1);
-            mGroupOverFlowState.copyFrom(resultState.getViewStateForView(overflowView));
+            mGroupOverFlowState.copyFrom(overflowView.getViewState());
 
             if (mContainingNotification.isOnAmbient()) {
                 mGroupOverFlowState.alpha = 0.0f;
@@ -724,7 +722,8 @@
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
     }
 
-    public void applyState(StackScrollState state) {
+    /** Applies state to children. */
+    public void applyState() {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = 0.0f;
@@ -737,7 +736,7 @@
                 && !mHideDividersDuringExpand);
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             viewState.applyToView(child);
 
             // layout the divider
@@ -799,14 +798,14 @@
      * This is called when the children expansion has changed and positions the children properly
      * for an appear animation.
      *
-     * @param state the new state we animate to
      */
-    public void prepareExpansionChanged(StackScrollState state) {
+    public void prepareExpansionChanged() {
         // TODO: do something that makes sense, like placing the invisible views correctly
         return;
     }
 
-    public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
+    /** Animate to a given state. */
+    public void startAnimationToState(AnimationProperties properties) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
         float expandFraction = getGroupExpandFraction();
@@ -816,7 +815,7 @@
                 && !mHideDividersDuringExpand);
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
-            ExpandableViewState viewState = state.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             viewState.animateTo(child, properties);
 
             // layout the divider
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 4d100a4..f0a2653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -45,34 +45,31 @@
 
     /**
      * Change the position of child to a new location
-     *
-     * @param child the view to change the position for
+     *  @param child the view to change the position for
      * @param newIndex the new index
      */
-    void changeViewPosition(View child, int newIndex);
+    void changeViewPosition(ExpandableView child, int newIndex);
 
     /**
      * Called when a child was added to a group.
      *
      * @param row row of the group child that was added
      */
-    void notifyGroupChildAdded(View row);
+    void notifyGroupChildAdded(ExpandableView row);
 
     /**
      * Called when a child was removed from a group.
-     *
-     * @param row row of the child that was removed
+     *  @param row row of the child that was removed
      * @param childrenContainer ViewGroup of the group that the child was removed from
      */
-    void notifyGroupChildRemoved(View row, ViewGroup childrenContainer);
+    void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer);
 
     /**
      * Generate an animation for an added child view.
-     *
-     * @param child The view to be added.
+     *  @param child The view to be added.
      * @param fromMoreCard Whether this add is coming from the "more" card on lockscreen.
      */
-    void generateAddAnimation(View child, boolean fromMoreCard);
+    void generateAddAnimation(ExpandableView child, boolean fromMoreCard);
 
     /**
      * Generate a child order changed event.
@@ -118,9 +115,9 @@
     /**
      * Handle snapping a non-dismissable row back if the user tried to dismiss it.
      *
-     * @param row row to snap back
+     * @param entry the entry whose row needs to snap back
      */
-    void snapViewIfNeeded(ExpandableNotificationRow row);
+    void snapViewIfNeeded(NotificationData.Entry entry);
 
     /**
      * Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
@@ -149,9 +146,9 @@
      * Called when a notification is removed from the shade. This cleans up the state for a
      * given view.
      *
-     * @param view view to clean up view state for
+     * @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
      */
-    void cleanUpViewState(View view);
+    void cleanUpViewStateForEntry(NotificationData.Entry entry);
 
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index c867a41..4f0831f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,13 +16,12 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-        .NUM_SECTIONS;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
 
-import android.view.View;
-
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.HashSet;
@@ -37,7 +36,7 @@
     private ActivatableNotificationView[] mLastInSectionViews;
     private ActivatableNotificationView[] mTmpFirstInSectionViews;
     private ActivatableNotificationView[] mTmpLastInSectionViews;
-    private HashSet<View> mAnimatedChildren;
+    private HashSet<ExpandableView> mAnimatedChildren;
     private Runnable mRoundingChangedCallback;
     private ExpandableNotificationRow mTrackedHeadsUp;
     private float mAppearFraction;
@@ -50,13 +49,13 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, false /* animate */);
+    public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), false /* animate */);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
-        updateView(headsUp, true /* animate */);
+    public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+        updateView(headsUp.getRow(), true /* animate */);
     }
 
     public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
@@ -211,7 +210,7 @@
         return anyChanged;
     }
 
-    public void setAnimatedChildren(HashSet<View> animatedChildren) {
+    public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
         mAnimatedChildren = animatedChildren;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ecd0d98..eca1a14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
         .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator
+        .ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
 
 import android.animation.Animator;
@@ -208,16 +210,12 @@
      */
     protected final StackScrollAlgorithm mStackScrollAlgorithm;
 
-    /**
-     * The current State this Layout is in
-     */
-    private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
     private final AmbientState mAmbientState;
     private NotificationGroupManager mGroupManager;
-    private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
+    private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>();
     private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
-    private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
-    private ArrayList<View> mChildrenChangingPositions = new ArrayList<>();
+    private ArrayList<ExpandableView> mChildrenToRemoveAnimated = new ArrayList<>();
+    private ArrayList<ExpandableView> mChildrenChangingPositions = new ArrayList<>();
     private HashSet<View> mFromMoreCardAdditions = new HashSet<>();
     private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<>();
     private ArrayList<View> mSwipedOutViews = new ArrayList<>();
@@ -275,7 +273,7 @@
     private boolean mDontReportNextOverScroll;
     private boolean mDontClampNextScroll;
     private boolean mNeedViewResizeAnimation;
-    private View mExpandedGroupView;
+    private ExpandableView mExpandedGroupView;
     private boolean mEverythingNeedsAnimation;
 
     /**
@@ -382,7 +380,7 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
-    private View mNeedingPulseAnimation;
+    private ExpandableView mNeedingPulseAnimation;
 
     /**
      * @see #setDarkAmount(float, float)
@@ -600,12 +598,12 @@
             public void setRemoteInputActive(NotificationData.Entry entry,
                     boolean remoteInputActive) {
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                entry.row.notifyHeightChanged(true /* needsAnimation */);
+                entry.notifyHeightChanged(true /* needsAnimation */);
                 updateFooter();
             }
 
             public void lockScrollTo(NotificationData.Entry entry) {
-                NotificationStackScrollLayout.this.lockScrollTo(entry.row);
+                NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
             }
 
             public void requestDisallowLongPressAndDismiss() {
@@ -620,7 +618,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Dependency.get(StatusBarStateController.class)
-                .addListener(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
+                .addCallback(mStateListener, StatusBarStateController.RANK_STACK_SCROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
@@ -628,7 +626,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
 
@@ -897,8 +895,10 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
-    public boolean isInVisibleLocation(ExpandableNotificationRow row) {
-        ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
+    public boolean isInVisibleLocation(NotificationData.Entry entry) {
+        ExpandableNotificationRow row = entry.getRow();
+        ExpandableViewState childViewState = row.getViewState();
+
         if (childViewState == null) {
             return false;
         }
@@ -944,7 +944,7 @@
                 ? 0
                 : mScroller.getCurrVelocity());
         mAmbientState.setScrollY(mOwnScrollY);
-        mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
+        mStackScrollAlgorithm.resetViewStates(mAmbientState);
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
             applyCurrentState();
         } else {
@@ -1213,12 +1213,12 @@
         if (topEntry == null) {
             return 0;
         }
-        ExpandableNotificationRow row = topEntry.row;
+        ExpandableNotificationRow row = topEntry.getRow();
         if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
+            final NotificationData.Entry groupSummary
                     = mGroupManager.getGroupSummary(row.getStatusBarNotification());
             if (groupSummary != null) {
-                row = groupSummary;
+                row = groupSummary.getRow();
             }
         }
         return row.getPinnedHeadsUpHeight();
@@ -1390,11 +1390,12 @@
                     && touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
+                    NotificationData.Entry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().row != row
+                            && mHeadsUpManager.getTopEntry().getRow() != row
                             && mGroupManager.getGroupSummary(
-                            mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
-                            != row) {
+                                mHeadsUpManager.getTopEntry().notification)
+                            != entry) {
                         continue;
                     }
                     return row.getViewAtPosition(touchY - childTop);
@@ -1524,7 +1525,8 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void snapViewIfNeeded(ExpandableNotificationRow child) {
+    public void snapViewIfNeeded(NotificationData.Entry entry) {
+        ExpandableNotificationRow child = entry.getRow();
         boolean animate = mIsExpanded || isPinnedHeadsUp(child);
         // If the child is showing the notification menu snap to that
         float targetLeft = child.getProvider().isMenuVisible() ? child.getTranslation() : 0;
@@ -1934,12 +1936,12 @@
      * @return the last child which has visibility unequal to GONE
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    public View getLastChildNotGone() {
+    public ExpandableView getLastChildNotGone() {
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
             if (child.getVisibility() != View.GONE && child != mShelf) {
-                return child;
+                return (ExpandableView) child;
             }
         }
         return null;
@@ -2508,35 +2510,33 @@
         // we only call our internal methods if this is actually a removal and not just a
         // notification which becomes a child notification
         if (!mChildTransferInProgress) {
-            onViewRemovedInternal(child, this);
+            onViewRemovedInternal((ExpandableView) child, this);
         }
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     @Override
-    public void cleanUpViewState(View child) {
+    public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+        View child = entry.getRow();
         if (child == mSwipeHelper.getTranslatingParentView()) {
             mSwipeHelper.clearTranslatingParentView();
         }
-        mCurrentStackScrollState.removeViewStateForView(child);
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void onViewRemovedInternal(View child, ViewGroup container) {
+    private void onViewRemovedInternal(ExpandableView child, ViewGroup container) {
         if (mChangePositionInProgress) {
             // This is only a position change, don't do anything special
             return;
         }
-        ExpandableView expandableView = (ExpandableView) child;
-        expandableView.setOnHeightChangedListener(null);
-        mCurrentStackScrollState.removeViewStateForView(child);
-        updateScrollStateForRemovedChild(expandableView);
+        child.setOnHeightChangedListener(null);
+        updateScrollStateForRemovedChild(child);
         boolean animationGenerated = generateRemoveAnimation(child);
         if (animationGenerated) {
             if (!mSwipedOutViews.contains(child)
-                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+                    || Math.abs(child.getTranslation()) != child.getWidth()) {
                 container.addTransientView(child, 0);
-                expandableView.setTransientContainer(container);
+                child.setTransientContainer(container);
             }
         } else {
             mSwipedOutViews.remove(child);
@@ -2580,14 +2580,14 @@
      * @return Whether an animation was generated.
      */
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    private boolean generateRemoveAnimation(View child) {
+    private boolean generateRemoveAnimation(ExpandableView child) {
         if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
             mAddedHeadsUpChildren.remove(child);
             return false;
         }
         if (isClickedHeadsUp(child)) {
             // An animation is already running, add it transiently
-            mClearTransientViewsWhenFinished.add((ExpandableView) child);
+            mClearTransientViewsWhenFinished.add(child);
             return true;
         }
         if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
@@ -2644,9 +2644,9 @@
     private boolean isChildInInvisibleGroup(View child) {
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            ExpandableNotificationRow groupSummary =
+            NotificationData.Entry groupSummary =
                     mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null && groupSummary != row) {
+            if (groupSummary != null && groupSummary.getRow() != row) {
                 return row.getVisibility() == View.INVISIBLE;
             }
         }
@@ -2761,7 +2761,7 @@
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void onViewAdded(View child) {
         super.onViewAdded(child);
-        onViewAddedInternal(child);
+        onViewAddedInternal((ExpandableView) child);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -2831,31 +2831,28 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void onViewAddedInternal(View child) {
+    private void onViewAddedInternal(ExpandableView child) {
         updateHideSensitiveForChild(child);
-        ((ExpandableView) child).setOnHeightChangedListener(this);
+        child.setOnHeightChangedListener(this);
         generateAddAnimation(child, false /* fromMoreCard */);
         updateAnimationState(child);
         updateChronometerForChild(child);
     }
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private void updateHideSensitiveForChild(View child) {
-        if (child instanceof ExpandableView) {
-            ExpandableView expandableView = (ExpandableView) child;
-            expandableView.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
-        }
+    private void updateHideSensitiveForChild(ExpandableView child) {
+        child.setHideSensitiveForIntrinsicHeight(mAmbientState.isHideSensitive());
     }
 
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
+    public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {
         onViewRemovedInternal(row, childrenContainer);
     }
 
     @Override
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void notifyGroupChildAdded(View row) {
+    public void notifyGroupChildAdded(ExpandableView row) {
         onViewAddedInternal(row);
     }
 
@@ -2927,7 +2924,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void generateAddAnimation(View child, boolean fromMoreCard) {
+    public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {
         if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
             // Generate Animations
             mChildrenToAddAnimated.add(child);
@@ -2944,7 +2941,7 @@
 
     @Override
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    public void changeViewPosition(View child, int newIndex) {
+    public void changeViewPosition(ExpandableView child, int newIndex) {
         int currentIndex = indexOfChild(child);
 
         if (currentIndex == -1) {
@@ -2983,8 +2980,7 @@
         }
         if (!mAnimationEvents.isEmpty() || isCurrentlyAnimating()) {
             setAnimationRunning(true);
-            mStateAnimator.startAnimationForEvents(mAnimationEvents, mCurrentStackScrollState,
-                    mGoToFullShadeDelay);
+            mStateAnimator.startAnimationForEvents(mAnimationEvents, mGoToFullShadeDelay);
             mAnimationEvents.clear();
             updateBackground();
             updateViewShadows();
@@ -3031,7 +3027,7 @@
                     continue;
                 }
             } else {
-                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
+                ExpandableViewState viewState = row.getViewState();
                 if (viewState == null) {
                     // A view state was never generated for this view, so we don't need to animate
                     // this. This may happen with notification children.
@@ -3097,7 +3093,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildRemovalEvents() {
-        for (View child : mChildrenToRemoveAnimated) {
+        for (ExpandableView child : mChildrenToRemoveAnimated) {
             boolean childWasSwipedOut = mSwipedOutViews.contains(child);
 
             // we need to know the view after this one
@@ -3139,7 +3135,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generatePositionChangeEvents() {
-        for (View child : mChildrenChangingPositions) {
+        for (ExpandableView child : mChildrenChangingPositions) {
             mAnimationEvents.add(new AnimationEvent(child,
                     AnimationEvent.ANIMATION_TYPE_CHANGE_POSITION));
         }
@@ -3153,7 +3149,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void generateChildAdditionEvents() {
-        for (View child : mChildrenToAddAnimated) {
+        for (ExpandableView child : mChildrenToAddAnimated) {
             if (mFromMoreCardAdditions.contains(child)) {
                 mAnimationEvents.add(new AnimationEvent(child,
                         AnimationEvent.ANIMATION_TYPE_ADD,
@@ -3245,7 +3241,7 @@
 
     @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
     protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
-        return new StackScrollAlgorithm(context);
+        return new StackScrollAlgorithm(context, this);
     }
 
     /**
@@ -3910,7 +3906,7 @@
             mStatusBar.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+            ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
             if (draggedViews.size() > 0) {
                 draggedViews.clear();
                 updateContinuousShadowDrawing();
@@ -4194,7 +4190,12 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void applyCurrentState() {
-        mCurrentStackScrollState.apply();
+        int numChildren = getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            child.applyViewState();
+        }
+
         if (mListener != null) {
             mListener.onChildLocationsChanged();
         }
@@ -4662,6 +4663,11 @@
         mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
     }
 
+    public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+        ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
+        generateHeadsUpAnimation(row, isHeadsUp);
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
         if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
@@ -4933,8 +4939,7 @@
             if (!(child instanceof ExpandableNotificationRow)) {
                 pw.println("  " + child.getClass().getSimpleName());
                 // Notifications dump it's viewstate as part of their dump to support children
-                ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(
-                        child);
+                ExpandableViewState viewState = child.getViewState();
                 if (viewState == null) {
                     pw.println("    no viewState!!!");
                 } else {
@@ -4951,7 +4956,7 @@
             ExpandableView child = (ExpandableView) getTransientView(i);
             child.dump(fd, pw, args);
         }
-        ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
+        ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
         int draggedCount = draggedViews.size();
         pw.println("  Dragged Views: " + draggedCount);
         for (int i = 0; i < draggedCount; i++) {
@@ -5090,7 +5095,7 @@
             if (i == 0) {
                 endRunnable = animationFinishAction;
             }
-            dismissViewAnimated(view, endRunnable, totalDelay, 260);
+            dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
             currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
             totalDelay += currentDelay;
         }
@@ -5487,7 +5492,7 @@
         static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
 
         final long eventStartTime;
-        final View changingView;
+        final ExpandableView mChangingView;
         final int animationType;
         final AnimationFilter filter;
         final long length;
@@ -5495,21 +5500,21 @@
         int darkAnimationOriginIndex;
         boolean headsUpFromBottom;
 
-        AnimationEvent(View view, int type) {
+        AnimationEvent(ExpandableView view, int type) {
             this(view, type, LENGTHS[type]);
         }
 
-        AnimationEvent(View view, int type, AnimationFilter filter) {
+        AnimationEvent(ExpandableView view, int type, AnimationFilter filter) {
             this(view, type, LENGTHS[type], filter);
         }
 
-        AnimationEvent(View view, int type, long length) {
+        AnimationEvent(ExpandableView view, int type, long length) {
             this(view, type, length, FILTERS[type]);
         }
 
-        AnimationEvent(View view, int type, long length, AnimationFilter filter) {
+        AnimationEvent(ExpandableView view, int type, long length, AnimationFilter filter) {
             eventStartTime = AnimationUtils.currentAnimationTimeMillis();
-            changingView = view;
+            mChangingView = view;
             animationType = type;
             this.length = length;
             this.filter = filter;
@@ -5692,7 +5697,7 @@
                         && (parent.areGutsExposed()
                         || mSwipeHelper.getExposedMenuView() == parent
                         || (parent.getNotificationChildren().size() == 1
-                        && parent.isClearable()))) {
+                        && parent.getEntry().isClearable()))) {
                     // In this case the group is expanded and showing the menu for the
                     // group, further interaction should apply to the group, not any
                     // child notifications so we use the parent of the child. We also do the same
@@ -5707,7 +5712,7 @@
         public void onBeginDrag(View v) {
             mFalsingManager.onNotificatonStartDismissing();
             setSwipingInProgress(true);
-            mAmbientState.onBeginDrag(v);
+            mAmbientState.onBeginDrag((ExpandableView) v);
             updateContinuousShadowDrawing();
             requestChildrenUpdate();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 87c361a..25fb7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -42,6 +42,7 @@
 public class StackScrollAlgorithm {
 
     private static final String LOG_TAG = "StackScrollAlgorithm";
+    private final ViewGroup mHostView;
 
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
@@ -55,7 +56,8 @@
     private float mHeadsUpInset;
     private int mPinnedZTranslationExtra;
 
-    public StackScrollAlgorithm(Context context) {
+    public StackScrollAlgorithm(Context context, ViewGroup hostView) {
+        mHostView = hostView;
         initView(context);
     }
 
@@ -79,49 +81,59 @@
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
     }
 
-    public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
+    /**
+     * Updates the state of all children in the hostview based on this algorithm.
+     */
+    public void resetViewStates(AmbientState ambientState) {
         // The state of the local variables are saved in an algorithmState to easily subdivide it
         // into multiple phases.
         StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
 
         // First we reset the view states to their default values.
-        resultState.resetViewStates();
+        resetChildViewStates();
 
-        initAlgorithmState(resultState, algorithmState, ambientState);
+        initAlgorithmState(mHostView, algorithmState, ambientState);
 
-        updatePositionsForState(resultState, algorithmState, ambientState);
+        updatePositionsForState(algorithmState, ambientState);
 
-        updateZValuesForState(resultState, algorithmState, ambientState);
+        updateZValuesForState(algorithmState, ambientState);
 
-        updateHeadsUpStates(resultState, algorithmState, ambientState);
+        updateHeadsUpStates(algorithmState, ambientState);
 
-        updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
-        updateClipping(resultState, algorithmState, ambientState);
-        updateSpeedBumpState(resultState, algorithmState, ambientState);
-        updateShelfState(resultState, ambientState);
-        getNotificationChildrenStates(resultState, algorithmState, ambientState);
+        updateDimmedActivatedHideSensitive(ambientState, algorithmState);
+        updateClipping(algorithmState, ambientState);
+        updateSpeedBumpState(algorithmState, ambientState);
+        updateShelfState(ambientState);
+        getNotificationChildrenStates(algorithmState, ambientState);
     }
 
-    private void getNotificationChildrenStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState,
+    private void resetChildViewStates() {
+        int numChildren = mHostView.getChildCount();
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
+            child.resetViewState();
+        }
+    }
+
+    private void getNotificationChildrenStates(StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = algorithmState.visibleChildren.get(i);
             if (v instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                row.getChildrenStates(resultState, ambientState);
+                row.updateChildrenStates(ambientState);
             }
         }
     }
 
-    private void updateSpeedBumpState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateSpeedBumpState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         int belowSpeedBump = ambientState.getSpeedBumpIndex();
         for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = child.getViewState();
 
             // The speed bump can also be gone, so equality needs to be taken when comparing
             // indices.
@@ -129,15 +141,16 @@
         }
 
     }
-    private void updateShelfState(StackScrollState resultState, AmbientState ambientState) {
+
+    private void updateShelfState(AmbientState ambientState) {
         NotificationShelf shelf = ambientState.getShelf();
         if (shelf != null) {
-            shelf.updateState(resultState, ambientState);
+            shelf.updateState(ambientState);
         }
     }
 
-    private void updateClipping(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateClipping(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding()
                 + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange()
                 : 0;
@@ -146,7 +159,7 @@
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState state = resultState.getViewStateForView(child);
+            ExpandableViewState state = child.getViewState();
             if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
                 previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
                 previousNotificationStart = Math.max(drawStart, previousNotificationStart);
@@ -190,15 +203,15 @@
      * Updates the dimmed, activated and hiding sensitive states of the children.
      */
     private void updateDimmedActivatedHideSensitive(AmbientState ambientState,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState) {
         boolean dimmed = ambientState.isDimmed();
         boolean dark = ambientState.isFullyDark();
         boolean hideSensitive = ambientState.isHideSensitive();
         View activatedChild = ambientState.getActivatedChild();
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
-            ExpandableViewState childViewState = resultState.getViewStateForView(child);
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            ExpandableViewState childViewState = child.getViewState();
             childViewState.dimmed = dimmed;
             childViewState.dark = dark;
             childViewState.hideSensitive = hideSensitive;
@@ -212,7 +225,7 @@
     /**
      * Initialize the algorithm state like updating the visible children.
      */
-    private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+    private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
             AmbientState ambientState) {
         float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
 
@@ -224,7 +237,6 @@
         state.scrollY = (int) (scrollY + bottomOverScroll);
 
         //now init the visible children and update paddings
-        ViewGroup hostView = resultState.getHostView();
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
         state.visibleChildren.ensureCapacity(childCount);
@@ -249,7 +261,7 @@
                     // we need normal padding now, to be in sync with what the stack calculates
                     lastView = null;
                 }
-                notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
+                notGoneIndex = updateNotGoneIndex(state, notGoneIndex, v);
                 float increasedPadding = v.getIncreasedPaddingAmount();
                 if (increasedPadding != 0.0f) {
                     state.paddingMap.put(v, increasedPadding);
@@ -282,13 +294,11 @@
                     ExpandableNotificationRow row = (ExpandableNotificationRow) v;
 
                     // handle the notgoneIndex for the children as well
-                    List<ExpandableNotificationRow> children =
-                            row.getNotificationChildren();
+                    List<ExpandableNotificationRow> children = row.getNotificationChildren();
                     if (row.isSummaryWithChildren() && children != null) {
                         for (ExpandableNotificationRow childRow : children) {
                             if (childRow.getVisibility() != View.GONE) {
-                                ExpandableViewState childState
-                                        = resultState.getViewStateForView(childRow);
+                                ExpandableViewState childState = childRow.getViewState();
                                 childState.notGoneIndex = notGoneIndex;
                                 notGoneIndex++;
                             }
@@ -301,8 +311,8 @@
         ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
         state.indexOfExpandingNotification = expandingNotification != null
                 ? expandingNotification.isChildInGroup()
-                    ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
-                    : state.visibleChildren.indexOf(expandingNotification)
+                ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+                : state.visibleChildren.indexOf(expandingNotification)
                 : -1;
     }
 
@@ -322,10 +332,9 @@
         }
     }
 
-    private int updateNotGoneIndex(StackScrollState resultState,
-            StackScrollAlgorithmState state, int notGoneIndex,
+    private int updateNotGoneIndex(StackScrollAlgorithmState state, int notGoneIndex,
             ExpandableView v) {
-        ExpandableViewState viewState = resultState.getViewStateForView(v);
+        ExpandableViewState viewState = v.getViewState();
         viewState.notGoneIndex = notGoneIndex;
         state.visibleChildren.add(v);
         notGoneIndex++;
@@ -335,27 +344,27 @@
     /**
      * Determine the positions for the views. This is the main part of the algorithm.
      *
-     * @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The current ambient state
+     * @param ambientState   The current ambient state
      */
-    private void updatePositionsForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updatePositionsForState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
 
         // The y coordinate of the current child.
         float currentYPosition = -algorithmState.scrollY;
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
-            currentYPosition = updateChild(i, resultState, algorithmState, ambientState,
-                    currentYPosition);
+            currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
         }
     }
 
-    protected float updateChild(int i, StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState,
+    protected float updateChild(
+            int i,
+            StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState,
             float currentYPosition) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        ExpandableViewState childViewState = child.getViewState();
         childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
         int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
         int childHeight = getMaxAllowedChildHeight(child);
@@ -404,8 +413,8 @@
         return algorithmState.getPaddingAfterChild(child);
     }
 
-    private void updateHeadsUpStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateHeadsUpStates(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         ExpandableNotificationRow topHeadsUpEntry = null;
         for (int i = 0; i < childCount; i++) {
@@ -417,7 +426,7 @@
             if (!row.isHeadsUp()) {
                 break;
             }
-            ExpandableViewState childState = resultState.getViewStateForView(row);
+            ExpandableViewState childState = row.getViewState();
             if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
                 topHeadsUpEntry = row;
                 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
@@ -439,7 +448,8 @@
                 childState.yTranslation = Math.max(childState.yTranslation, mHeadsUpInset);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 childState.hidden = false;
-                ExpandableViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+                ExpandableViewState topState =
+                        topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
                 if (topState != null && !isTopEntry && (!mIsExpanded
                         || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
@@ -491,9 +501,8 @@
      * Clamp the height of the child down such that its end is at most on the beginning of
      * the shelf.
      *
-     * @param child
      * @param childViewState the view state of the child
-     * @param ambientState the ambient state
+     * @param ambientState   the ambient state
      */
     private void clampPositionToShelf(ExpandableView child,
             ExpandableViewState childViewState,
@@ -521,31 +530,31 @@
             ExpandableView expandableView = (ExpandableView) child;
             return expandableView.getIntrinsicHeight();
         }
-        return child == null? mCollapsedSize : child.getHeight();
+        return child == null ? mCollapsedSize : child.getHeight();
     }
 
     /**
      * Calculate the Z positions for all children based on the number of items in both stacks and
      * save it in the resultState
-     *  @param resultState The result state to update the zTranslation values
+     *
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     * @param ambientState The ambient state of the algorithm
+     * @param ambientState   The ambient state of the algorithm
      */
-    private void updateZValuesForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateZValuesForState(StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         float childrenOnTop = 0.0f;
         for (int i = childCount - 1; i >= 0; i--) {
             childrenOnTop = updateChildZValue(i, childrenOnTop,
-                    resultState, algorithmState, ambientState);
+                    algorithmState, ambientState);
         }
     }
 
     protected float updateChildZValue(int i, float childrenOnTop,
-            StackScrollState resultState, StackScrollAlgorithmState algorithmState,
+            StackScrollAlgorithmState algorithmState,
             AmbientState ambientState) {
         ExpandableView child = algorithmState.visibleChildren.get(i);
-        ExpandableViewState childViewState = resultState.getViewStateForView(child);
+        ExpandableViewState childViewState = child.getViewState();
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
         float baseZ = ambientState.getBaseZHeight();
         if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
deleted file mode 100644
index e55707c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollState.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification.stack;
-
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-
-import java.util.List;
-import java.util.WeakHashMap;
-
-/**
- * A state of a
- * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout} which
- * can be applied to a viewGroup.
- */
-public class StackScrollState {
-
-    private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
-
-    private final ViewGroup mHostView;
-    private WeakHashMap<ExpandableView, ExpandableViewState> mStateMap;
-
-    public StackScrollState(ViewGroup hostView) {
-        mHostView = hostView;
-        mStateMap = new WeakHashMap<>();
-    }
-
-    public ViewGroup getHostView() {
-        return mHostView;
-    }
-
-    public void resetViewStates() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            resetViewState(child);
-
-            // handling reset for child notifications
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                List<ExpandableNotificationRow> children =
-                        row.getNotificationChildren();
-                if (row.isSummaryWithChildren() && children != null) {
-                    for (ExpandableNotificationRow childRow : children) {
-                        resetViewState(childRow);
-                    }
-                }
-            }
-        }
-    }
-
-    private void resetViewState(ExpandableView view) {
-        ExpandableViewState viewState = mStateMap.get(view);
-        if (viewState == null) {
-            viewState = view.createNewViewState(this);
-            mStateMap.put(view, viewState);
-        }
-        // initialize with the default values of the view
-        viewState.height = view.getIntrinsicHeight();
-        viewState.gone = view.getVisibility() == View.GONE;
-        viewState.alpha = 1f;
-        viewState.notGoneIndex = -1;
-        viewState.xTranslation = view.getTranslationX();
-        viewState.hidden = false;
-        viewState.scaleX = view.getScaleX();
-        viewState.scaleY = view.getScaleY();
-        viewState.inShelf = false;
-        viewState.headsUpIsVisible = false;
-    }
-
-    public ExpandableViewState getViewStateForView(View requestedView) {
-        return mStateMap.get(requestedView);
-    }
-
-    public void removeViewStateForView(View child) {
-        mStateMap.remove(child);
-    }
-
-    /**
-     * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
-     * The properties are only applied if they effectively changed.
-     */
-    public void apply() {
-        int numChildren = mHostView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
-            ExpandableViewState state = mStateMap.get(child);
-            if (state == null) {
-                Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
-                        "to the hostView");
-                continue;
-            }
-            if (state.gone) {
-                continue;
-            }
-            state.applyToView(child);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 34dab53..d690547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -44,6 +44,7 @@
     public static final int ANIMATION_DURATION_WAKEUP = 500;
     public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
+    public static final int ANIMATION_DURATION_SWIPE = 260;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
     public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
     public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
@@ -128,25 +129,25 @@
 
     public void startAnimationForEvents(
             ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
-            StackScrollState finalState, long additionalDelay) {
+            long additionalDelay) {
 
-        processAnimationEvents(mAnimationEvents, finalState);
+        processAnimationEvents(mAnimationEvents);
 
         int childCount = mHostLayout.getChildCount();
         mAnimationFilter.applyCombination(mNewEvents);
         mCurrentAdditionalDelay = additionalDelay;
         mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
-        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
+        mCurrentLastNotAddedIndex = findLastNotAddedIndex();
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             if (viewState == null || child.getVisibility() == View.GONE
-                    || applyWithoutAnimation(child, viewState, finalState)) {
+                    || applyWithoutAnimation(child, viewState)) {
                 continue;
             }
 
-            initAnimationProperties(finalState, child, viewState);
+            initAnimationProperties(child, viewState);
             viewState.animateTo(child, mAnimationProperties);
         }
         if (!isRunning()) {
@@ -159,7 +160,7 @@
         mNewAddChildren.clear();
     }
 
-    private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
+    private void initAnimationProperties(ExpandableView child,
             ExpandableViewState viewState) {
         boolean wasAdded = mAnimationProperties.wasAdded(child);
         mAnimationProperties.duration = mCurrentLength;
@@ -173,7 +174,7 @@
                         || viewState.clipTopAmount != child.getClipTopAmount()
                         || viewState.dark != child.isDark())) {
             mAnimationProperties.delay = mCurrentAdditionalDelay
-                    + calculateChildAnimationDelay(viewState, finalState);
+                    + calculateChildAnimationDelay(viewState);
         }
     }
 
@@ -193,8 +194,7 @@
      *
      * @return true if no animation should be performed
      */
-    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
-            StackScrollState finalState) {
+    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState) {
         if (mShadeExpanded) {
             return false;
         }
@@ -214,12 +214,12 @@
         return true;
     }
 
-    private int findLastNotAddedIndex(StackScrollState finalState) {
+    private int findLastNotAddedIndex() {
         int childCount = mHostLayout.getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
 
-            ExpandableViewState viewState = finalState.getViewStateForView(child);
+            ExpandableViewState viewState = child.getViewState();
             if (viewState == null || child.getVisibility() == View.GONE) {
                 continue;
             }
@@ -230,8 +230,7 @@
         return -1;
     }
 
-    private long calculateChildAnimationDelay(ExpandableViewState viewState,
-            StackScrollState finalState) {
+    private long calculateChildAnimationDelay(ExpandableViewState viewState) {
         if (mAnimationFilter.hasGoToFullShadeEvent) {
             return calculateDelayGoToFullShade(viewState);
         }
@@ -244,8 +243,8 @@
             switch (event.animationType) {
                 case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
                     int ownIndex = viewState.notGoneIndex;
-                    int changingIndex = finalState
-                            .getViewStateForView(event.changingView).notGoneIndex;
+                    int changingIndex =
+                            ((ExpandableView) (event.mChangingView)).getViewState().notGoneIndex;
                     int difference = Math.abs(ownIndex - changingIndex);
                     difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
                             difference - 1));
@@ -258,9 +257,9 @@
                 case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
                     int ownIndex = viewState.notGoneIndex;
                     boolean noNextView = event.viewAfterChangingView == null;
-                    View viewAfterChangingView = noNextView
+                    ExpandableView viewAfterChangingView = noNextView
                             ? mHostLayout.getLastChildNotGone()
-                            : event.viewAfterChangingView;
+                            : (ExpandableView) event.viewAfterChangingView;
                     if (viewAfterChangingView == null) {
                         // This can happen when the last view in the list is removed.
                         // Since the shelf is still around and the only view, the code still goes
@@ -268,8 +267,7 @@
                         // have changed.
                         continue;
                     }
-                    int nextIndex = finalState
-                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
+                    int nextIndex = viewAfterChangingView.getViewState().notGoneIndex;
                     if (ownIndex >= nextIndex) {
                         // we only have the view afterwards
                         ownIndex++;
@@ -351,20 +349,17 @@
     /**
      * Process the animationEvents for a new animation
      *
-     * @param animationEvents the animation events for the animation to perform
-     * @param finalState the final state to animate to
+     *  @param animationEvents the animation events for the animation to perform
      */
     private void processAnimationEvents(
-            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
-            StackScrollState finalState) {
+            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents) {
         for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
-            final ExpandableView changingView = (ExpandableView) event.changingView;
+            final ExpandableView changingView = (ExpandableView) event.mChangingView;
             if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
 
                 // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState == null || viewState.gone) {
                     // The position for this child was never generated, let's continue.
                     continue;
@@ -381,12 +376,9 @@
 
                 // Find the amount to translate up. This is needed in order to understand the
                 // direction of the remove animation (either downwards or upwards)
-                ExpandableViewState viewState = finalState
-                        .getViewStateForView(event.viewAfterChangingView);
-                int actualHeight = changingView.getActualHeight();
                 // upwards by default
                 float translationDirection = -1.0f;
-                if (viewState != null) {
+                if (event.viewAfterChangingView != null) {
                     float ownPosition = changingView.getTranslationY();
                     if (changingView instanceof ExpandableNotificationRow
                             && event.viewAfterChangingView instanceof ExpandableNotificationRow) {
@@ -402,8 +394,11 @@
                             ownPosition = changingRow.getTranslationWhenRemoved();
                         }
                     }
+                    int actualHeight = changingView.getActualHeight();
                     // there was a view after this one, Approximate the distance the next child
                     // travelled
+                    ExpandableViewState viewState =
+                            ((ExpandableView) event.viewAfterChangingView).getViewState();
                     translationDirection = ((viewState.yTranslation
                             - (ownPosition + actualHeight / 2.0f)) * 2 /
                             actualHeight);
@@ -426,11 +421,11 @@
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
-                row.prepareExpansionChanged(finalState);
+                ExpandableNotificationRow row = (ExpandableNotificationRow) event.mChangingView;
+                row.prepareExpansionChanged();
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState != null) {
                     mTmpState.copyFrom(viewState);
                     mTmpState.yTranslation += mPulsingAppearingTranslation;
@@ -439,7 +434,7 @@
                 }
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_PULSE_DISAPPEAR) {
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 if (viewState != null) {
                     viewState.alpha = 0;
                     // We want to animate the alpha away before the view starts translating,
@@ -454,7 +449,7 @@
             } else if (event.animationType == NotificationStackScrollLayout
                     .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
                 // This item is added, initialize it's properties.
-                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
+                ExpandableViewState viewState = changingView.getViewState();
                 mTmpState.copyFrom(viewState);
                 if (event.headsUpFromBottom) {
                     mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
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 3b13fe9..8418cba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -121,14 +121,14 @@
     public void onResume() {
         super.onResume();
         mCommandQueue.addCallbacks(this);
-        mStatusBarStateController.addListener(this);
+        mStatusBarStateController.addCallback(this);
     }
 
     @Override
     public void onPause() {
         super.onPause();
         mCommandQueue.removeCallbacks(this);
-        mStatusBarStateController.removeListener(this);
+        mStatusBarStateController.removeCallback(this);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 94b2cde..cfa751c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -88,7 +88,7 @@
     public DozeScrimController(DozeParameters dozeParameters) {
         mDozeParameters = dozeParameters;
         //Never expected to be destroyed
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 40f9f45..3c8cad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -143,9 +143,9 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     /** To count the distance from the window right boundary to scroller right boundary. The
@@ -298,9 +298,9 @@
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
         updateTopEntry();
-        updateHeader(headsUp.getEntry());
+        updateHeader(entry);
     }
 
     public void setExpandedHeight(float expandedHeight, float appearFraction) {
@@ -339,7 +339,7 @@
     }
 
     public void updateHeader(NotificationData.Entry entry) {
-        ExpandableNotificationRow row = entry.row;
+        ExpandableNotificationRow row = entry.getRow();
         float headerVisibleAmount = 1.0f;
         if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
             headerVisibleAmount = mExpandFraction;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 00d6b14..aa0b7b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -129,7 +129,7 @@
                 updateTouchableRegionListener();
             }
         });
-        Dependency.get(StatusBarStateController.class).addListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
         mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
             if (!hasBubbles) {
                 mBubbleGoingAway = true;
@@ -143,7 +143,7 @@
     }
 
     public void destroy() {
-        Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
+        Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
     }
 
     private void initResources() {
@@ -304,18 +304,19 @@
             return;
         }
         if (hasPinnedHeadsUp()) {
-            ExpandableNotificationRow topEntry = getTopEntry().row;
+            NotificationData.Entry topEntry = getTopEntry();
             if (topEntry.isChildInGroup()) {
-                final ExpandableNotificationRow groupSummary
-                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+                final NotificationData.Entry groupSummary
+                        = mGroupManager.getGroupSummary(topEntry.notification);
                 if (groupSummary != null) {
                     topEntry = groupSummary;
                 }
             }
-            topEntry.getLocationOnScreen(mTmpTwoArray);
+            ExpandableNotificationRow topRow = topEntry.getRow();
+            topRow.getLocationOnScreen(mTmpTwoArray);
             int minX = mTmpTwoArray[0];
-            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int height = topEntry.getIntrinsicHeight();
+            int maxX = mTmpTwoArray[0] + topRow.getWidth();
+            int height = topRow.getIntrinsicHeight();
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(minX, 0, maxX, mHeadsUpInset + height);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index be4df45..9c1c71a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -84,8 +84,8 @@
                     // We might touch above the visible heads up child, but then we still would
                     // like to capture it.
                     NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
-                    if (topEntry != null && topEntry.row.isPinned()) {
-                        mPickedChild = topEntry.row;
+                    if (topEntry != null && topEntry.isRowPinned()) {
+                        mPickedChild = topEntry.getRow();
                         mTouchingHeadsUpView = true;
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index d8280ba..a81b7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -22,6 +22,7 @@
 import android.content.res.Resources;
 import android.util.MathUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -229,6 +230,11 @@
                 - mBurnInPreventionOffsetX;
     }
 
+    @VisibleForTesting
+    void setPulsingPadding(int padding) {
+        mPulsingPadding = padding;
+    }
+
     public static class Result {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index b29889d..1c6d292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -74,14 +74,14 @@
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
                 .addCallbacks(this);
-        mStatusBarStateController.addListener(this);
+        mStatusBarStateController.addCallback(this);
         mDozeAmount = mStatusBarStateController.getDozeAmount();
     }
 
     public void destroy(Context context) {
         SysUiServiceProvider.getComponent(context, CommandQueue.class)
                 .removeCallbacks(this);
-        mStatusBarStateController.removeListener(this);
+        mStatusBarStateController.removeCallback(this);
     }
 
     public void saveState(Bundle outState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
index 1002f9e..b83ebc7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-
 import android.annotation.NonNull;
 import android.hardware.input.InputManager;
 import android.os.Handler;
@@ -35,10 +33,8 @@
  */
 public class NavigationBackAction extends NavigationGestureAction {
 
-    private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback";
     private static final String BACK_AFTER_END_PROP =
             "quickstepcontroller_homegoesbackwhenend";
-    private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
     private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60;
     private static final long BACK_GESTURE_POLL_TIMEOUT = 1000;
 
@@ -60,23 +56,13 @@
     }
 
     @Override
-    public int requiresTouchDownHitTarget() {
-        return HIT_TARGET_HOME;
-    }
-
-    @Override
-    public boolean requiresDragWithHitTarget() {
-        return true;
-    }
-
-    @Override
     public boolean canPerformAction() {
         return mProxySender.getBackButtonAlpha() > 0;
     }
 
     @Override
     public boolean isEnabled() {
-        return swipeHomeGoBackGestureEnabled();
+        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED);
     }
 
     @Override
@@ -110,13 +96,8 @@
         mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
     }
 
-    private boolean swipeHomeGoBackGestureEnabled() {
-        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
-                && getGlobalBoolean(PULL_HOME_GO_BACK_PROP);
-    }
-
     private boolean shouldExecuteBackOnUp() {
-        return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED)
+        return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED)
                 && getGlobalBoolean(BACK_AFTER_END_PROP);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 12a0cc8..3984405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -24,7 +24,6 @@
 import android.view.Display;
 import android.view.IWallpaperVisibilityListener;
 import android.view.IWindowManager;
-import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -165,23 +164,4 @@
         }
         mView.onDarkIntensityChange(darkIntensity);
     }
-
-    private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View v, MotionEvent ev) {
-            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-                // even though setting the systemUI visibility below will turn these views
-                // on, we need them to come up faster so that they can catch this motion
-                // event
-                applyLightsOut(false, false, false);
-
-                try {
-                    mBarService.setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE,
-                            "LightsOutListener");
-                } catch (android.os.RemoteException ex) {
-                }
-            }
-            return false;
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5db43ea..cd6e1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
@@ -77,6 +78,8 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction;
+import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 
@@ -146,6 +149,8 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
+    private NavigationPrototypeController mPrototypeController;
+    private NavigationGestureAction[] mDefaultGestureMap;
     private QuickScrubAction mQuickScrubAction;
     private QuickStepAction mQuickStepAction;
     private NavigationBackAction mBackAction;
@@ -261,6 +266,18 @@
         }
     };
 
+    private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() {
+        @Override
+        public void onGestureRemap(int[] actions) {
+            updateNavigationGestures();
+        }
+
+        @Override
+        public void onBackButtonVisibilityChanged(boolean visible) {
+            getBackButton().setVisibility(visible ? VISIBLE : GONE);
+        }
+    };
+
     public NavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -309,6 +326,14 @@
         mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService);
         mQuickStepAction = new QuickStepAction(this, mOverviewProxyService);
         mBackAction = new NavigationBackAction(this, mOverviewProxyService);
+        mDefaultGestureMap = new NavigationGestureAction[] {
+                mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */,
+                mQuickScrubAction
+        };
+
+        mPrototypeController = new NavigationPrototypeController(mHandler, mContext);
+        mPrototypeController.register();
+        mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener);
     }
 
     public BarTransitions getBarTransitions() {
@@ -323,8 +348,32 @@
         mPanelView = panel;
         if (mGestureHelper instanceof QuickStepController) {
             ((QuickStepController) mGestureHelper).setComponents(this);
-            ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction,
-                    null /* swipeDownAction*/, mBackAction, mQuickScrubAction);
+            updateNavigationGestures();
+        }
+    }
+
+    private void updateNavigationGestures() {
+        if (mGestureHelper instanceof QuickStepController) {
+            final int[] assignedMap = mPrototypeController.getGestureActionMap();
+            ((QuickStepController) mGestureHelper).setGestureActions(
+                    getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]),
+                    getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]),
+                    getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]),
+                    getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3]));
+        }
+    }
+
+    private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType,
+            NavigationGestureAction defaultAction) {
+        switch(actionType) {
+            case NavigationPrototypeController.ACTION_QUICKSTEP:
+                return mQuickStepAction;
+            case NavigationPrototypeController.ACTION_QUICKSCRUB:
+                return mQuickScrubAction;
+            case NavigationPrototypeController.ACTION_BACK:
+                return mBackAction;
+            default:
+                return defaultAction;
         }
     }
 
@@ -902,10 +951,10 @@
     private void updateTaskSwitchHelper() {
         if (mGestureHelper == null) return;
         boolean isRtl = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-        int navBarPos = 0;
+        int navBarPos = NAV_BAR_INVALID;
         try {
-            // TODO: Use WindowManagerService.getNavBarPosition(int displayId)
-            navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition();
+            navBarPos = WindowManagerGlobal.getWindowManagerService().getNavBarPosition(
+                    mDisplay.getDisplayId());
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to get nav bar position.", e);
         }
@@ -1043,6 +1092,7 @@
         if (mGestureHelper != null) {
             mGestureHelper.destroy();
         }
+        mPrototypeController.unregister();
         setUpSwipeUpOnboarding(false);
         for (int i = 0; i < mButtonDispatchers.size(); ++i) {
             mButtonDispatchers.valueAt(i).onDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
index 83067f6..a8d00c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationGestureAction.java
@@ -127,6 +127,15 @@
     }
 
     /**
+     * Decide if the controller should not send the current motion event to launcher via
+     * {@link OverviewProxyService}
+     * @return if controller should not proxy
+     */
+    public boolean disableProxyEvents() {
+        return false;
+    }
+
+    /**
      * Tell if action is enabled. Compared to {@link #canPerformAction()} this is based on settings
      * if the action is disabled for a particular gesture. For example a back action can be enabled
      * however if there is nothing to back to then {@link #canPerformAction()} should return false.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
new file mode 100644
index 0000000..e8c0bf1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 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.phone;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different
+ * prototypes to run in the system. The class will handle communication changes from the settings
+ * app and call back to listeners.
+ */
+public class NavigationPrototypeController extends ContentObserver {
+    private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback";
+
+    static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled";
+    private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map";
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK})
+    @interface GestureAction {}
+    static final int ACTION_DEFAULT = 0;
+    static final int ACTION_QUICKSTEP = 1;
+    static final int ACTION_QUICKSCRUB = 2;
+    static final int ACTION_BACK = 3;
+
+    private OnPrototypeChangedListener mListener;
+
+    /**
+     * Each index corresponds to a different action set in QuickStepController
+     * {@see updateSwipeLTRBackSetting}
+     */
+    private int[] mActionMap = new int[4];
+
+    private final Context mContext;
+
+    public NavigationPrototypeController(Handler handler, Context context) {
+        super(handler);
+        mContext = context;
+        updateSwipeLTRBackSetting();
+    }
+
+    public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Observe all the settings to react to from prototype settings
+     */
+    public void register() {
+        registerObserver(HIDE_BACK_BUTTON_SETTING);
+        registerObserver(GESTURE_MATCH_SETTING);
+    }
+
+    /**
+     * Disable observing settings to react to from prototype settings
+     */
+    public void unregister() {
+        mContext.getContentResolver().unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        super.onChange(selfChange, uri);
+        if (!selfChange && mListener != null) {
+            try {
+                final String path = uri.getPath();
+                if (path.endsWith(GESTURE_MATCH_SETTING)) {
+                    // Get the settings gesture map corresponding to each action
+                    // {@see updateSwipeLTRBackSetting}
+                    updateSwipeLTRBackSetting();
+                    mListener.onGestureRemap(mActionMap);
+                } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) {
+                    mListener.onBackButtonVisibilityChanged(
+                            !getGlobalBool(HIDE_BACK_BUTTON_SETTING));
+                }
+            } catch (SettingNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    /**
+     * Retrieve the action map to apply to the quick step controller
+     * @return an action map
+     */
+    int[] getGestureActionMap() {
+        return mActionMap;
+    }
+
+    /**
+     * Since Settings.Global cannot pass arrays, use a string to represent each character as a
+     * gesture map to actions corresponding to {@see GestureAction}. The number is represented as:
+     * Number: [up] [down] [left] [right]
+     */
+    private void updateSwipeLTRBackSetting() {
+        String value = Settings.Global.getString(mContext.getContentResolver(),
+                GESTURE_MATCH_SETTING);
+        if (value != null) {
+            for (int i = 0; i < mActionMap.length; ++i) {
+                mActionMap[i] = Character.getNumericValue(value.charAt(i));
+            }
+        }
+    }
+
+    private boolean getGlobalBool(String name) throws SettingNotFoundException {
+        return Settings.Global.getInt(mContext.getContentResolver(), name) == 1;
+    }
+
+    private void registerObserver(String name) {
+        mContext.getContentResolver()
+                .registerContentObserver(Settings.Global.getUriFor(name), false, this);
+    }
+
+    public interface OnPrototypeChangedListener {
+        void onGestureRemap(@GestureAction int[] actions);
+        void onBackButtonVisibilityChanged(boolean visible);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index c74514e..2a68fa5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -82,7 +82,7 @@
     private boolean mIsDozing;
 
     public NotificationGroupAlertTransferHelper() {
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
@@ -198,7 +198,7 @@
                 alertNotificationWhenPossible(entry, getActiveAlertManager());
             } else {
                 // The transfer is no longer valid. Free the content.
-                entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+                entry.getRow().freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
             }
         }
     }
@@ -299,9 +299,9 @@
 
         Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
         if (child != null) {
-            if (child.row.keepInParent()
-                    || child.row.isRemoved()
-                    || child.row.isDismissed()) {
+            if (child.getRow().keepInParent()
+                    || child.isRowRemoved()
+                    || child.isRowDismissed()) {
                 // The notification is actually already removed. No need to alert it.
                 return;
             }
@@ -390,10 +390,10 @@
     private void alertNotificationWhenPossible(@NonNull Entry entry,
             @NonNull AlertingNotificationManager alertManager) {
         @InflationFlag int contentFlag = alertManager.getContentFlag();
-        if (!entry.row.isInflationFlagSet(contentFlag)) {
+        if (!entry.getRow().isInflationFlagSet(contentFlag)) {
             mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
-            entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
-            entry.row.inflateViews();
+            entry.getRow().updateInflationFlag(contentFlag, true /* shouldInflate */);
+            entry.getRow().inflateViews();
             return;
         }
         if (alertManager.isAlerting(entry.key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 8ceabf8..8f4369a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -55,7 +55,7 @@
     private boolean mIsUpdatingUnchangedGroup;
 
     public NotificationGroupManager() {
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     /**
@@ -87,8 +87,7 @@
         group.expanded = expanded;
         if (group.summary != null) {
             for (OnGroupChangeListener listener : mListeners) {
-                listener.onGroupExpansionChanged(group.summary.row,
-                        expanded);
+                listener.onGroupExpansionChanged(group.summary.getRow(), expanded);
             }
         }
     }
@@ -133,7 +132,7 @@
     }
 
     public void onEntryAdded(final NotificationData.Entry added) {
-        if (added.row.isRemoved()) {
+        if (added.isRowRemoved()) {
             added.setDebugThrowable(new Throwable());
         }
         final StatusBarNotification sbn = added.notification;
@@ -152,17 +151,17 @@
             if (existing != null && existing != added) {
                 Throwable existingThrowable = existing.getDebugThrowable();
                 Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
-                        + "existing removed: " + existing.row.isRemoved()
+                        + "existing removed: " + existing.isRowRemoved()
                         + (existingThrowable != null
                                 ? Log.getStackTraceString(existingThrowable) + "\n": "")
-                        + " added removed" + added.row.isRemoved()
+                        + " added removed" + added.isRowRemoved()
                         , new Throwable());
             }
             group.children.put(added.key, added);
             updateSuppression(group);
         } else {
             group.summary = added;
-            group.expanded = added.row.areChildrenExpanded();
+            group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
                 ArrayList<NotificationData.Entry> childrenCopy
@@ -263,9 +262,9 @@
         if (!isOnlyChild(sbn)) {
             return false;
         }
-        ExpandableNotificationRow logicalGroupSummary = getLogicalGroupSummary(sbn);
+        NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
         return logicalGroupSummary != null
-                && !logicalGroupSummary.getStatusBarNotification().equals(sbn);
+                && !logicalGroupSummary.notification.equals(sbn);
     }
 
     private int getTotalNumberOfChildren(StatusBarNotification sbn) {
@@ -339,7 +338,7 @@
      * Get the summary of a specified status bar notification. For isolated notification this return
      * itself.
      */
-    public ExpandableNotificationRow getGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(getGroupKey(sbn));
     }
 
@@ -348,16 +347,17 @@
      * but the logical summary, i.e when a child is isolated, it still returns the summary as if
      * it wasn't isolated.
      */
-    public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
+    public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
         return getGroupSummary(sbn.getGroupKey());
     }
 
     @Nullable
-    private ExpandableNotificationRow getGroupSummary(String groupKey) {
+    private NotificationData.Entry getGroupSummary(String groupKey) {
         NotificationGroup group = mGroupMap.get(groupKey);
+        //TODO: see if this can become an Entry
         return group == null ? null
                 : group.summary == null ? null
-                        : group.summary.row;
+                        : group.summary;
     }
 
     /**
@@ -438,11 +438,11 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -533,8 +533,7 @@
 
     private boolean isGroupNotFullyVisible(NotificationGroup notificationGroup) {
         return notificationGroup.summary == null
-                || notificationGroup.summary.row.getClipTopAmount() > 0
-                || notificationGroup.summary.row.getTranslationY() < 0;
+                || notificationGroup.summary.isGroupNotFullyVisible();
     }
 
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
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 184766c..2d5d562 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -192,13 +192,13 @@
                 && !mEntryManager.getNotificationData().isHighPriority(entry.notification)) {
             return false;
         }
-        if (!StatusBar.isTopLevelChild(entry)) {
+        if (!entry.isTopLevelChild()) {
             return false;
         }
-        if (entry.row.getVisibility() == View.GONE) {
+        if (entry.getRow().getVisibility() == View.GONE) {
             return false;
         }
-        if (entry.row.isDismissed() && hideDismissed) {
+        if (entry.isRowDismissed() && hideDismissed) {
             return false;
         }
 
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 851e6d0..a2a11bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -366,7 +366,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
         Dependency.get(ZenModeController.class).addCallback(this);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
@@ -375,7 +375,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).removeListener(this);
+        Dependency.get(StatusBarStateController.class).removeCallback(this);
         Dependency.get(ZenModeController.class).removeCallback(this);
         Dependency.get(ConfigurationController.class).removeCallback(this);
     }
@@ -2500,25 +2500,26 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
+        mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
 
         // When we're unpinning the notification via active edge they remain heads-upped,
         // we need to make sure that an animation happens in this case, otherwise the notification
         // will stick to the top without any interaction.
-        if (isFullyCollapsed() && headsUp.isHeadsUp()) {
-            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, false);
-            headsUp.setHeadsUpIsVisible();
+        if (isFullyCollapsed() && entry.isRowHeadsUp()) {
+            mNotificationStackScroller.generateHeadsUpAnimation(
+                    entry.getHeadsUpAnimationView(), false);
+            entry.setHeadsUpIsVisible();
         }
     }
 
     @Override
     public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+        mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
index 74744f1..2b202eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubAction.java
@@ -212,6 +212,11 @@
     }
 
     @Override
+    public boolean disableProxyEvents() {
+        return true;
+    }
+
+    @Override
     protected void onGestureStart(MotionEvent event) {
         updateHighlight();
         ObjectAnimator trackAnimator = ObjectAnimator.ofPropertyValuesHolder(this,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 0eff4d4..4983618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone;
 
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT;
 
@@ -30,9 +31,9 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
@@ -100,6 +101,7 @@
     private NavigationGestureAction mCurrentAction;
     private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
 
+    private final Rect mLastLayoutRect = new Rect();
     private final OverviewProxyService mOverviewEventSender;
     private final Context mContext;
     private final StatusBar mStatusBar;
@@ -107,7 +109,6 @@
     private final Matrix mTransformLocalMatrix = new Matrix();
 
     public QuickStepController(Context context) {
-        final Resources res = context.getResources();
         mContext = context;
         mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
         mOverviewEventSender = Dependency.get(OverviewProxyService.class);
@@ -142,6 +143,8 @@
             if (action != null) {
                 action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive);
                 action.onDarkIntensityChange(mDarkIntensity);
+                action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top,
+                        mLastLayoutRect.right, mLastLayoutRect.bottom);
             }
         }
     }
@@ -182,6 +185,7 @@
 
         // Requires proxy and an active gesture or able to perform any gesture to continue
         if (mOverviewEventSender.getProxy() == null
+                || !mOverviewEventSender.shouldShowSwipeUpUI()
                 || (mCurrentAction == null && !canPerformAnyAction())) {
             return deadZoneConsumed;
         }
@@ -272,25 +276,21 @@
                         if (mDragVPositive ? (posV < touchDownV) : (posV > touchDownV)) {
                             // Swiping up gesture
                             tryToStartGesture(mGestureActions[ACTION_SWIPE_UP_INDEX],
-                                    false /* alignedWithNavBar */, false /* positiveDirection */,
-                                    event);
+                                    false /* alignedWithNavBar */, event);
                         } else {
                             // Swiping down gesture
                             tryToStartGesture(mGestureActions[ACTION_SWIPE_DOWN_INDEX],
-                                    false /* alignedWithNavBar */, true /* positiveDirection */,
-                                    event);
+                                    false /* alignedWithNavBar */, event);
                         }
                     } else if (exceededSwipeHorizontalTouchSlop) {
                         if (mDragHPositive ? (posH < touchDownH) : (posH > touchDownH)) {
                             // Swiping left (ltr) gesture
                             tryToStartGesture(mGestureActions[ACTION_SWIPE_LEFT_INDEX],
-                                    true /* alignedWithNavBar */, false /* positiveDirection */,
-                                    event);
+                                    true /* alignedWithNavBar */, event);
                         } else {
                             // Swiping right (ltr) gesture
                             tryToStartGesture(mGestureActions[ACTION_SWIPE_RIGHT_INDEX],
-                                    true /* alignedWithNavBar */, true /* positiveDirection */,
-                                    event);
+                                    true /* alignedWithNavBar */, event);
                         }
                     }
                 }
@@ -303,7 +303,6 @@
             case MotionEvent.ACTION_UP:
                 if (mCurrentAction != null) {
                     mCurrentAction.endGesture();
-                    mCurrentAction = null;
                 }
 
                 // Return the hit target back to its original position
@@ -326,6 +325,11 @@
         if (shouldProxyEvents(action)) {
             proxyMotionEvents(event);
         }
+
+        // Clear action when gesture and event proxy finishes
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+            mCurrentAction = null;
+        }
         return mCurrentAction != null || deadZoneConsumed;
     }
 
@@ -351,8 +355,7 @@
 
     private boolean shouldProxyEvents(int action) {
         final boolean actionValid = (mCurrentAction == null
-                || (mGestureActions[ACTION_SWIPE_UP_INDEX] != null
-                        && mGestureActions[ACTION_SWIPE_UP_INDEX].isActive()));
+                || !mCurrentAction.disableProxyEvents());
         if (actionValid && !mIsInScreenPinning) {
             // Allow down, cancel and up events, move and other events are passed if notifications
             // are not showing and disabled gestures (such as long press) are not executed
@@ -382,6 +385,7 @@
                 action.onLayout(changed, left, top, right, bottom);
             }
         }
+        mLastLayoutRect.set(left, top, right, bottom);
     }
 
     @Override
@@ -418,6 +422,9 @@
                 mDragHPositive = !isRTL;
                 mDragVPositive = true;
                 break;
+            case NAV_BAR_INVALID:
+                Log.e(TAG, "Invalid nav bar position");
+                break;
         }
 
         for (NavigationGestureAction action: mGestureActions) {
@@ -448,7 +455,7 @@
     }
 
     private void tryToStartGesture(NavigationGestureAction action, boolean alignedWithNavBar,
-            boolean positiveDirection, MotionEvent event) {
+            MotionEvent event) {
         if (action == null) {
             return;
         }
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 bdddf5b..58b9de4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
@@ -160,7 +161,6 @@
 import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -195,7 +195,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -474,8 +473,10 @@
                 return;
             }
             WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
-            final boolean supportsAmbientMode = info != null &&
-                    info.supportsAmbientMode();
+            final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+            final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+                    && info != null && info.supportsAmbientMode();
 
             mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
             mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
@@ -484,7 +485,7 @@
 
     private Runnable mLaunchTransitionEndRunnable;
     protected boolean mLaunchTransitionFadingAway;
-    private ExpandableNotificationRow mDraggedDownRow;
+    private NotificationData.Entry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -626,7 +627,7 @@
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
         mColorExtractor.addOnColorsChangedListener(this);
-        mStatusBarStateController.addListener(this, StatusBarStateController.RANK_STATUS_BAR);
+        mStatusBarStateController.addCallback(this, StatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -721,7 +722,7 @@
         IWallpaperManager wallpaperManager = IWallpaperManager.Stub.asInterface(
                 ServiceManager.getService(Context.WALLPAPER_SERVICE));
         try {
-            wallpaperManager.setInAmbientMode(false /* ambientMode */, false /* animated */);
+            wallpaperManager.setInAmbientMode(false /* ambientMode */, 0L /* duration */);
         } catch (RemoteException e) {
             // Just pass, nothing critical.
         }
@@ -843,16 +844,7 @@
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
-
-        try {
-            boolean showNav = mWindowManagerService.hasNavigationBar();
-            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
-            if (showNav) {
-                createNavigationBar();
-            }
-        } catch (RemoteException ex) {
-            // no window manager? good luck with that
-        }
+        createNavigationBar();
 
         if (ENABLE_LOCKSCREEN_WALLPAPER) {
             mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler);
@@ -927,8 +919,7 @@
                     Dependency.get(ExtensionController.class)
                             .newExtension(QS.class)
                             .withPlugin(QS.class)
-                            .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new)
-                            .withDefault(QSFragment::new)
+                            .withDefault(this::createDefaultQSFragment)
                             .build());
             final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
                     mIconController);
@@ -1016,6 +1007,10 @@
         ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f));
     }
 
+    protected QS createDefaultQSFragment() {
+        return new QSFragment();
+    }
+
     protected void setUpPresenter() {
         // Set up the initial notification state.
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
@@ -1061,13 +1056,24 @@
     }
 
     protected void createNavigationBar() {
-        mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
-            mNavigationBar = (NavigationBarFragment) fragment;
-            if (mLightBarController != null) {
-                mNavigationBar.setLightBarController(mLightBarController);
-            }
-            mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
-        });
+        try {
+            // TODO(117478341): Move this into DisplayNavigationBarController#createNavigationBars
+            // for-loop. We will also move the whole navigation bar logic together.
+            final boolean showNav = mWindowManagerService.hasNavigationBar(DEFAULT_DISPLAY);
+            if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
+            if (!showNav) return;
+
+            mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
+                mNavigationBar = (NavigationBarFragment) fragment;
+                if (mLightBarController != null) {
+                    mNavigationBar.setLightBarController(mLightBarController);
+                }
+                mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
+            });
+        } catch (RemoteException ex) {
+            // no window manager? good luck with that
+        }
+        mNavigationBarController.createNavigationBars();
     }
 
     /**
@@ -1209,7 +1215,8 @@
         }
         int dockSide = WindowManagerProxy.getInstance().getDockSide();
         if (dockSide == WindowManager.DOCKED_INVALID) {
-            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();
+            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(
+                    mDisplay.getDisplayId());
             if (navbarPos == NAV_BAR_POS_INVALID) {
                 return false;
             }
@@ -1265,10 +1272,6 @@
         mQSPanel.clickTile(tile);
     }
 
-    public static boolean isTopLevelChild(Entry entry) {
-        return entry.row.getParent() instanceof NotificationStackScrollLayout;
-    }
-
     public boolean areNotificationsHidden() {
         return mZenController.areNotificationsHiddenInShade();
     }
@@ -1491,12 +1494,12 @@
     }
 
     @Override
-    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpPinned(NotificationData.Entry entry) {
         dismissVolumeDialog();
     }
 
     @Override
-    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    public void onHeadsUpUnPinned(NotificationData.Entry entry) {
     }
 
     @Override
@@ -2605,9 +2608,7 @@
         final int notificationCount = activeNotifications.size();
         for (int i = 0; i < notificationCount; i++) {
             NotificationData.Entry entry = activeNotifications.get(i);
-            if (entry.row != null) {
-                entry.row.resetUserExpansion();
-            }
+            entry.resetUserExpansion();
         }
     }
 
@@ -2888,7 +2889,7 @@
         mContext.unregisterReceiver(mDemoReceiver);
         mAssistManager.destroy();
         mHeadsUpManager.destroy();
-        mStatusBarStateController.removeListener(this);
+        mStatusBarStateController.removeCallback(this);
 
         if (mQSPanel != null && mQSPanel.getHost() != null) {
             mQSPanel.getHost().destroy();
@@ -3038,10 +3039,10 @@
             mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
         updatePanelExpansionForKeyguard();
-        if (mDraggedDownRow != null) {
-            mDraggedDownRow.setUserLocked(false);
-            mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
-            mDraggedDownRow = null;
+        if (mDraggedDownEntry != null) {
+            mDraggedDownEntry.setUserLocked(false);
+            mDraggedDownEntry.notifyHeightChanged(false /* needsAnimation */);
+            mDraggedDownEntry = null;
         }
     }
 
@@ -3181,9 +3182,9 @@
             }
             long delay = mKeyguardMonitor.calculateGoingToFullShadeDelay();
             mNotificationPanel.animateToFullShade(delay);
-            if (mDraggedDownRow != null) {
-                mDraggedDownRow.setUserLocked(false);
-                mDraggedDownRow = null;
+            if (mDraggedDownEntry != null) {
+                mDraggedDownEntry.setUserLocked(false);
+                mDraggedDownEntry = null;
             }
 
             // TODO(115978725): Support animations on external nav bars.
@@ -3575,14 +3576,15 @@
 
         int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
+        NotificationData.Entry entry = null;
         if (expandView instanceof ExpandableNotificationRow) {
-            row = (ExpandableNotificationRow) expandView;
-            row.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
+            entry = ((ExpandableNotificationRow) expandView).getEntry();
+            entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
-            row.setGroupExpansionChanging(true);
-            if (row.getStatusBarNotification() != null) {
-                userId = row.getStatusBarNotification().getUserId();
+            entry.setGroupExpansionChanging(true);
+            if (entry.notification != null) {
+                userId = entry.notification.getUserId();
             }
         }
         boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
@@ -3592,7 +3594,7 @@
         if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
             showBouncerIfKeyguard();
-            mDraggedDownRow = row;
+            mDraggedDownEntry = entry;
             mPendingRemoteInputView = null;
         } else {
             mNotificationPanel.animateToFullShade(0 /* delay */);
@@ -4326,7 +4328,14 @@
         }, afterKeyguardGone);
     }
 
+    @Override
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
+        startPendingIntentDismissingKeyguard(intent, null);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(
+            final PendingIntent intent, @Nullable final Runnable intentSentCallback) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -4345,6 +4354,9 @@
             if (intent.isActivity()) {
                 mAssistManager.hideAssist();
             }
+            if (intentSentCallback != null) {
+                intentSentCallback.run();
+            }
         }, afterKeyguardGone);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 484fe11..0f8970f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -151,7 +151,7 @@
         mLockPatternUtils = lockPatternUtils;
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
-        Dependency.get(StatusBarStateController.class).addListener(this);
+        Dependency.get(StatusBarStateController.class).addCallback(this);
     }
 
     public void registerStatusBar(StatusBar statusBar,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index edfc049..588c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -418,7 +418,7 @@
             StatusBarNotification parentToCancel = null;
             if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
                 StatusBarNotification summarySbn =
-                        mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification();
+                        mGroupManager.getLogicalGroupSummary(sbn).notification;
                 if (shouldAutoCancel(summarySbn)) {
                     parentToCancel = summarySbn;
                 }
@@ -591,7 +591,7 @@
     public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
         mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
-            mShadeController.goToLockedShade(clickedEntry.row);
+            mShadeController.goToLockedShade(clickedEntry.getRow());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a743d41e..3f1e826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -70,7 +70,7 @@
         mContext = context;
         mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
                 new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
-        mStatusBarStateController.addListener(mStateListener);
+        mStatusBarStateController.addCallback(mStateListener);
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mCommandQueue = getComponent(context, CommandQueue.class);
         mCommandQueue.addCallbacks(this);
@@ -206,7 +206,7 @@
     }
 
     @Override
-    public boolean handleRemoteViewClick(PendingIntent pendingIntent,
+    public boolean handleRemoteViewClick(View view, PendingIntent pendingIntent,
             NotificationRemoteInputManager.ClickHandler defaultHandler) {
         final boolean isActivity = pendingIntent.isActivity();
         if (isActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 62b6d91..986a86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -92,7 +92,7 @@
         mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
         mDozeParameters = dozeParameters;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
-        Dependency.get(StatusBarStateController.class).addListener(
+        Dependency.get(StatusBarStateController.class).addCallback(
                 mStateListener, StatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER);
         Dependency.get(ConfigurationController.class).addCallback(this);
     }
@@ -283,7 +283,6 @@
         applyModalFlag(state);
         applyBrightness(state);
         applyHasTopUi(state);
-        applySleepToken(state);
         applyNotTouchable(state);
         if (mLp.copyFrom(mLpChanged) != 0) {
             mWindowManager.updateViewLayout(mStatusBarView, mLp);
@@ -328,14 +327,6 @@
         mHasTopUiChanged = isExpanded(state);
     }
 
-    private void applySleepToken(State state) {
-        if (state.dozing) {
-            mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
-        } else {
-            mLpChanged.privateFlags &= ~LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
-        }
-    }
-
     private void applyNotTouchable(State state) {
         if (state.notTouchable) {
             mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 978a72d..53e461d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -49,6 +49,7 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.Window;
+import android.view.WindowInsetsController;
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -785,6 +786,11 @@
         @Override
         public void reportActivityRelaunched() {
         }
+
+        @Override
+        public WindowInsetsController getInsetsController() {
+            return null;
+        }
     };
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8517d90..c2af95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -165,7 +165,7 @@
         mClockVisibleByUser = bundle.getBoolean(VISIBLE_BY_USER, true);
         mShowSeconds = bundle.getBoolean(SHOW_SECONDS, false);
         if (bundle.containsKey(VISIBILITY)) {
-            setVisibility(bundle.getInt(VISIBILITY));
+            super.setVisibility(bundle.getInt(VISIBILITY));
         }
     }
 
@@ -203,6 +203,7 @@
 
         // Make sure we update to the current time
         updateClock();
+        updateClockVisibility();
         updateShowSeconds();
     }
 
@@ -247,6 +248,15 @@
         }
     };
 
+    @Override
+    public void setVisibility(int visibility) {
+        if (visibility == View.VISIBLE && !shouldBeVisible()) {
+            return;
+        }
+
+        super.setVisibility(visibility);
+    }
+
     public void setClockVisibleByUser(boolean visible) {
         mClockVisibleByUser = visible;
         updateClockVisibility();
@@ -257,11 +267,15 @@
         updateClockVisibility();
     }
 
+    private boolean shouldBeVisible() {
+        return mClockVisibleByPolicy && mClockVisibleByUser;
+    }
+
     private void updateClockVisibility() {
-        boolean visible = mClockVisibleByPolicy && mClockVisibleByUser;
+        boolean visible = shouldBeVisible();
         Dependency.get(IconLogger.class).onIconVisibility("clock", visible);
         int visibility = visible ? View.VISIBLE : View.GONE;
-        setVisibility(visibility);
+        super.setVisibility(visibility);
     }
 
     final void updateClock() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
index 639e50c..9c099f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EncryptionHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.os.SystemProperties;
+import android.sysprop.VoldProperties;
 
 /**
  * Helper for determining whether the phone is decrypted yet.
@@ -26,7 +26,7 @@
     public static final boolean IS_DATA_ENCRYPTED = isDataEncrypted();
 
     private static boolean isDataEncrypted() {
-        String voldState = SystemProperties.get("vold.decrypt");
+        String voldState = VoldProperties.decrypt().orElse("");
         return "1".equals(voldState) || "trigger_restart_min_framework".equals(voldState);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fdab616..e7280643 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -123,15 +123,15 @@
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setEntryPinned: " + isPinned);
         }
-        ExpandableNotificationRow row = headsUpEntry.mEntry.row;
-        if (row.isPinned() != isPinned) {
-            row.setPinned(isPinned);
+        NotificationData.Entry entry = headsUpEntry.mEntry;
+        if (entry.isRowPinned() != isPinned) {
+            entry.setRowPinned(isPinned);
             updatePinnedMode();
             for (OnHeadsUpChangedListener listener : mListeners) {
                 if (isPinned) {
-                    listener.onHeadsUpPinned(row);
+                    listener.onHeadsUpPinned(entry);
                 } else {
-                    listener.onHeadsUpUnPinned(row);
+                    listener.onHeadsUpUnPinned(entry);
                 }
             }
         }
@@ -144,7 +144,7 @@
     @Override
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(true);
+        entry.setHeadsUp(true);
         setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, true);
@@ -154,12 +154,12 @@
     @Override
     protected void onAlertEntryRemoved(AlertEntry alertEntry) {
         NotificationData.Entry entry = alertEntry.mEntry;
-        entry.row.setHeadsUp(false);
+        entry.setHeadsUp(false);
         setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
-        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+        entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
     }
 
     protected void updatePinnedMode() {
@@ -282,7 +282,7 @@
     private boolean hasPinnedNotificationInternal() {
         for (String key : mAlertEntries.keySet()) {
             AlertEntry entry = getHeadsUpEntry(key);
-            if (entry.mEntry.row.isPinned()) {
+            if (entry.mEntry.isRowPinned()) {
                 return true;
             }
         }
@@ -302,10 +302,9 @@
 
             // when the user unpinned all of HUNs by moving one HUN, all of HUNs should not stay
             // on the screen.
-            if (userUnPinned && entry.mEntry != null && entry.mEntry.row != null) {
-                ExpandableNotificationRow row = entry.mEntry.row;
-                if (row.mustStayOnScreen()) {
-                    row.setHeadsUpIsVisible();
+            if (userUnPinned && entry.mEntry != null) {
+                if (entry.mEntry.mustStayOnScreen()) {
+                    entry.mEntry.setHeadsUpIsVisible();
                 }
             }
         }
@@ -341,7 +340,7 @@
      */
     public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
-        if (headsUpEntry != null && entry.row.isPinned()) {
+        if (headsUpEntry != null && entry.isRowPinned()) {
             headsUpEntry.setExpanded(expanded);
         }
     }
@@ -365,15 +364,15 @@
 
         @Override
         protected boolean isSticky() {
-            return (mEntry.row.isPinned() && expanded)
+            return (mEntry.isRowPinned() && expanded)
                     || remoteInputActive || hasFullScreenIntent(mEntry);
         }
 
         @Override
         public int compareTo(@NonNull AlertEntry alertEntry) {
             HeadsUpEntry headsUpEntry = (HeadsUpEntry) alertEntry;
-            boolean isPinned = mEntry.row.isPinned();
-            boolean otherPinned = headsUpEntry.mEntry.row.isPinned();
+            boolean isPinned = mEntry.isRowPinned();
+            boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
             if (isPinned && !otherPinned) {
                 return -1;
             } else if (!isPinned && otherPinned) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index d434768..7ad547a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -33,12 +33,12 @@
     /**
      * A notification was just pinned to the top.
      */
-    default void onHeadsUpPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification was just unpinned from the top.
      */
-    default void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {}
+    default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
 
     /**
      * A notification just became a heads up or turned back to its normal state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index a485fa8..866015e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -245,7 +245,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        if (mEntry.row.isChangingPosition()) {
+        if (mEntry.getRow().isChangingPosition()) {
             if (getVisibility() == VISIBLE && mEditText.isFocusable()) {
                 mEditText.requestFocus();
             }
@@ -255,7 +255,7 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        if (mEntry.row.isChangingPosition() || isTemporarilyDetached()) {
+        if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
             return;
         }
         mController.removeRemoteInput(mEntry, mToken);
@@ -495,7 +495,7 @@
         }
 
         private void defocusIfNeeded(boolean animate) {
-            if (mRemoteInputView != null && mRemoteInputView.mEntry.row.isChangingPosition()
+            if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
                     || isTemporarilyDetached()) {
                 if (isTemporarilyDetached()) {
                     // We might get reattached but then the other one of HUN / expanded might steal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 88ff078..2a4336e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -21,7 +21,6 @@
 import android.text.method.TransformationMethod;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Size;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,6 +40,7 @@
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.PriorityQueue;
@@ -208,12 +208,14 @@
      * Add smart actions to be shown next to smart replies. Only the actions that fit into the
      * notification are shown.
      */
-    public void addSmartActions(SmartActions smartActions) {
+    public void addSmartActions(SmartActions smartActions,
+            SmartReplyController smartReplyController, NotificationData.Entry entry) {
         int numSmartActions = smartActions.actions.size();
         for (int n = 0; n < numSmartActions; n++) {
             Notification.Action action = smartActions.actions.get(n);
             if (action.actionIntent != null) {
-                Button actionButton = inflateActionButton(getContext(), this, action);
+                Button actionButton = inflateActionButton(
+                        getContext(), this, n, smartActions, smartReplyController, entry);
                 addView(actionButton);
             }
         }
@@ -270,47 +272,35 @@
     }
 
     @VisibleForTesting
-    Button inflateActionButton(Context context, ViewGroup root, Notification.Action action) {
+    Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
+            SmartActions smartActions, SmartReplyController smartReplyController,
+            NotificationData.Entry entry) {
+        Notification.Action action = smartActions.actions.get(actionIndex);
         Button button = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_action_button, root, false);
         button.setText(action.title);
 
         Drawable iconDrawable = action.getIcon().loadDrawable(context);
         // Add the action icon to the Smart Action button.
-        Size newIconSize = calculateIconSizeFromSingleLineButton(context, root,
-                new Size(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight()));
-        iconDrawable.setBounds(0, 0, newIconSize.getWidth(), newIconSize.getHeight());
+        int newIconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.smart_action_button_icon_size);
+        iconDrawable.setBounds(0, 0, newIconSize, newIconSize);
         button.setCompoundDrawables(iconDrawable, null, null, null);
 
         button.setOnClickListener(view ->
-                getActivityStarter().startPendingIntentDismissingKeyguard(action.actionIntent));
+                getActivityStarter().startPendingIntentDismissingKeyguard(
+                        action.actionIntent,
+                        () -> smartReplyController.smartActionClicked(
+                                entry, actionIndex, action, smartActions.fromAssistant)));
 
         // TODO(b/119010281): handle accessibility
 
+        // Mark this as an Action button
+        final LayoutParams lp = (LayoutParams) button.getLayoutParams();
+        lp.buttonType = SmartButtonType.ACTION;
         return button;
     }
 
-    private static Size calculateIconSizeFromSingleLineButton(Context context, ViewGroup root,
-            Size originalIconSize) {
-        Button button = (Button) LayoutInflater.from(context).inflate(
-                R.layout.smart_action_button, root, false);
-        // Add simple text here to ensure the button displays one line of text.
-        button.setText("a");
-        return calculateIconSizeFromButtonHeight(button, originalIconSize);
-    }
-
-    // Given a button with text on a single line - we want to add an icon to that button. This
-    // method calculates the icon height to use to avoid making the button grow in height.
-    private static Size calculateIconSizeFromButtonHeight(Button button, Size originalIconSize) {
-        // A completely permissive measure spec should make the button text single-line.
-        button.measure(MEASURE_SPEC_ANY_LENGTH, MEASURE_SPEC_ANY_LENGTH);
-        int buttonHeight = button.getMeasuredHeight();
-        int newIconHeight = buttonHeight / 2;
-        int newIconWidth = (int) (originalIconSize.getWidth()
-                * ((double) newIconHeight) / originalIconSize.getHeight());
-        return new Size(newIconWidth, newIconHeight);
-    }
-
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
@@ -344,18 +334,26 @@
         int displayedChildCount = 0;
         int buttonPaddingHorizontal = mSingleLineButtonPaddingHorizontal;
 
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
+        // Set up a list of suggestions where actions come before replies. Note that the Buttons
+        // themselves have already been added to the view hierarchy in an order such that Smart
+        // Replies are shown before Smart Actions. The order of the list below determines which
+        // suggestions will be shown at all - only the first X elements are shown (where X depends
+        // on how much space each suggestion button needs).
+        List<View> smartActions = filterActionsOrReplies(SmartButtonType.ACTION);
+        List<View> smartReplies = filterActionsOrReplies(SmartButtonType.REPLY);
+        List<View> smartSuggestions = new ArrayList<>(smartActions);
+        smartSuggestions.addAll(smartReplies);
+        List<View> coveredSuggestions = new ArrayList<>();
+
+        for (View child : smartSuggestions) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
-                continue;
-            }
 
             child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
                     buttonPaddingHorizontal, child.getPaddingBottom());
             child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
 
+            coveredSuggestions.add(child);
+
             final int lineCount = ((Button) child).getLineCount();
             if (lineCount < 1 || lineCount > 2) {
                 // If smart reply has no text, or more than two lines, then don't show it.
@@ -409,7 +407,8 @@
 
                     // Mark all buttons from the last squeezing round as "failed to squeeze", so
                     // that they're re-measured without squeezing later.
-                    markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_FAILED, i);
+                    markButtonsWithPendingSqueezeStatusAs(
+                            LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
 
                     // The current button doesn't fit, so there's no point in measuring further
                     // buttons.
@@ -418,7 +417,8 @@
 
                 // The current button fits, so mark all squeezed buttons as "successfully squeezed"
                 // to prevent them from being un-squeezed in a subsequent squeezing round.
-                markButtonsWithPendingSqueezeStatusAs(LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, i);
+                markButtonsWithPendingSqueezeStatusAs(
+                        LayoutParams.SQUEEZE_STATUS_SUCCESSFUL, coveredSuggestions);
             }
 
             lp.show = true;
@@ -437,6 +437,22 @@
                         mPaddingTop + maxChildHeight + mPaddingBottom), heightMeasureSpec));
     }
 
+    private List<View> filterActionsOrReplies(SmartButtonType buttonType) {
+        List<View> actions = new ArrayList<>();
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (child.getVisibility() != View.VISIBLE || !(child instanceof Button)) {
+                continue;
+            }
+            if (lp.buttonType == buttonType) {
+                actions.add(child);
+            }
+        }
+        return actions;
+    }
+
     private void resetButtonsLayoutParams() {
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -607,9 +623,9 @@
         }
     }
 
-    private void markButtonsWithPendingSqueezeStatusAs(int squeezeStatus, int maxChildIndex) {
-        for (int i = 0; i <= maxChildIndex; i++) {
-            final View child = getChildAt(i);
+    private void markButtonsWithPendingSqueezeStatusAs(
+            int squeezeStatus, List<View> coveredChildren) {
+        for (View child : coveredChildren) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (lp.squeezeStatus == LayoutParams.SQUEEZE_STATUS_PENDING) {
                 lp.squeezeStatus = squeezeStatus;
@@ -704,6 +720,11 @@
         return mActivityStarter;
     }
 
+    private enum SmartButtonType {
+        REPLY,
+        ACTION
+    }
+
     @VisibleForTesting
     static class LayoutParams extends ViewGroup.LayoutParams {
 
@@ -729,6 +750,7 @@
 
         private boolean show = false;
         private int squeezeStatus = SQUEEZE_STATUS_NONE;
+        private SmartButtonType buttonType = SmartButtonType.REPLY;
 
         private LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
deleted file mode 100644
index 9b616e0..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * Copyright (C) 2018 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.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorSet;
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.app.Dialog;
-import android.app.KeyguardManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.media.CarAudioManager;
-import android.car.media.ICarVolumeCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.ServiceConnection;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.Xml;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-
-import androidx.car.widget.ListItem;
-import androidx.car.widget.ListItemAdapter;
-import androidx.car.widget.ListItemAdapter.BackgroundStyle;
-import androidx.car.widget.ListItemProvider.ListProvider;
-import androidx.car.widget.PagedListView;
-import androidx.car.widget.SeekbarListItem;
-
-import com.android.systemui.R;
-import com.android.systemui.plugins.VolumeDialog;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Car version of the volume dialog.
- *
- * Methods ending in "H" must be called on the (ui) handler.
- */
-public class CarVolumeDialogImpl implements VolumeDialog {
-  private static final String TAG = Util.logTag(CarVolumeDialogImpl.class);
-
-  private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems";
-  private static final String XML_TAG_VOLUME_ITEM = "item";
-  private static final int HOVERING_TIMEOUT = 16000;
-  private static final int NORMAL_TIMEOUT = 3000;
-  private static final int LISTVIEW_ANIMATION_DURATION_IN_MILLIS = 250;
-  private static final int DISMISS_DELAY_IN_MILLIS = 50;
-  private static final int ARROW_FADE_IN_START_DELAY_IN_MILLIS = 100;
-
-  private final Context mContext;
-  private final H mHandler = new H();
-
-  private Window mWindow;
-  private CustomDialog mDialog;
-  private PagedListView mListView;
-  private ListItemAdapter mPagedListAdapter;
-  // All the volume items.
-  private final SparseArray<VolumeItem> mVolumeItems = new SparseArray<>();
-  // Available volume items in car audio manager.
-  private final List<VolumeItem> mAvailableVolumeItems = new ArrayList<>();
-  // Volume items in the PagedListView.
-  private final List<ListItem> mVolumeLineItems = new ArrayList<>();
-  private final KeyguardManager mKeyguard;
-
-  private Car mCar;
-  private CarAudioManager mCarAudioManager;
-
-  private boolean mHovering;
-  private boolean mShowing;
-  private boolean mExpanded;
-
-  public CarVolumeDialogImpl(Context context) {
-    mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
-    mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-    mCar = Car.createCar(mContext, mServiceConnection);
-  }
-
-  public void init(int windowType, Callback callback) {
-    initDialog();
-
-    mCar.connect();
-  }
-
-  @Override
-  public void destroy() {
-    mHandler.removeCallbacksAndMessages(null);
-
-    cleanupAudioManager();
-    // unregisterVolumeCallback is not being called when disconnect car, so we manually cleanup
-    // audio manager beforehand.
-    mCar.disconnect();
-  }
-
-  private void initDialog() {
-    loadAudioUsageItems();
-    mVolumeLineItems.clear();
-    mDialog = new CustomDialog(mContext);
-
-    mHovering = false;
-    mShowing = false;
-    mExpanded = false;
-    mWindow = mDialog.getWindow();
-    mWindow.requestFeature(Window.FEATURE_NO_TITLE);
-    mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-    mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
-        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
-    mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-    mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-    mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
-    final WindowManager.LayoutParams lp = mWindow.getAttributes();
-    lp.format = PixelFormat.TRANSLUCENT;
-    lp.setTitle(VolumeDialogImpl.class.getSimpleName());
-    lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
-    lp.windowAnimations = -1;
-    mWindow.setAttributes(lp);
-    mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-    mDialog.setCanceledOnTouchOutside(true);
-    mDialog.setContentView(R.layout.car_volume_dialog);
-    mDialog.setOnShowListener(dialog -> {
-      mListView.setTranslationY(-mListView.getHeight());
-      mListView.setAlpha(0);
-      mListView.animate()
-          .alpha(1)
-          .translationY(0)
-          .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
-          .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
-          .start();
-    });
-    mListView = (PagedListView) mWindow.findViewById(R.id.volume_list);
-    mListView.setOnHoverListener((v, event) -> {
-      int action = event.getActionMasked();
-      mHovering = (action == MotionEvent.ACTION_HOVER_ENTER)
-          || (action == MotionEvent.ACTION_HOVER_MOVE);
-      rescheduleTimeoutH();
-      return true;
-    });
-
-    mPagedListAdapter = new ListItemAdapter(mContext, new ListProvider(mVolumeLineItems),
-        BackgroundStyle.PANEL);
-    mListView.setAdapter(mPagedListAdapter);
-    mListView.setMaxPages(PagedListView.UNLIMITED_PAGES);
-  }
-
-  public void show(int reason) {
-    mHandler.obtainMessage(H.SHOW, reason).sendToTarget();
-  }
-
-  public void dismiss(int reason) {
-    mHandler.obtainMessage(H.DISMISS, reason).sendToTarget();
-  }
-
-  private void showH(int reason) {
-    if (D.BUG) {
-      Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
-    }
-
-    mHandler.removeMessages(H.SHOW);
-    mHandler.removeMessages(H.DISMISS);
-    rescheduleTimeoutH();
-    // Refresh the data set before showing.
-    mPagedListAdapter.notifyDataSetChanged();
-    if (mShowing) {
-      return;
-    }
-    mShowing = true;
-
-    mDialog.show();
-    Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
-  }
-
-  protected void rescheduleTimeoutH() {
-    mHandler.removeMessages(H.DISMISS);
-    final int timeout = computeTimeoutH();
-    mHandler.sendMessageDelayed(mHandler
-        .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout);
-
-    if (D.BUG) {
-      Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller());
-    }
-  }
-
-  private int computeTimeoutH() {
-    return mHovering ? HOVERING_TIMEOUT : NORMAL_TIMEOUT;
-  }
-
-  protected void dismissH(int reason) {
-    if (D.BUG) {
-      Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
-    }
-
-    mHandler.removeMessages(H.DISMISS);
-    mHandler.removeMessages(H.SHOW);
-    if (!mShowing) {
-      return;
-    }
-
-    mListView.animate().cancel();
-
-    mListView.setTranslationY(0);
-    mListView.setAlpha(1);
-    mListView.animate()
-        .alpha(0)
-        .translationY(-mListView.getHeight())
-        .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS)
-        .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
-        .withEndAction(() -> mHandler.postDelayed(() -> {
-          if (D.BUG) {
-            Log.d(TAG, "mDialog.dismiss()");
-          }
-          mDialog.dismiss();
-          mShowing = false;
-        }, DISMISS_DELAY_IN_MILLIS))
-        .start();
-
-    Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
-  }
-
-  public void dump(PrintWriter writer) {
-    writer.println(VolumeDialogImpl.class.getSimpleName() + " state:");
-    writer.print("  mShowing: "); writer.println(mShowing);
-  }
-
-  private void loadAudioUsageItems() {
-    try (XmlResourceParser parser = mContext.getResources().getXml(R.xml.car_volume_items)) {
-      AttributeSet attrs = Xml.asAttributeSet(parser);
-      int type;
-      // Traverse to the first start tag
-      while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
-          && type != XmlResourceParser.START_TAG) {
-      }
-
-      if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) {
-        throw new RuntimeException("Meta-data does not start with carVolumeItems tag");
-      }
-      int outerDepth = parser.getDepth();
-      int rank = 0;
-      while ((type=parser.next()) != XmlResourceParser.END_DOCUMENT
-          && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) {
-        if (type == XmlResourceParser.END_TAG) {
-          continue;
-        }
-        if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) {
-          TypedArray item = mContext.getResources().obtainAttributes(
-              attrs, R.styleable.carVolumeItems_item);
-          int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1);
-          if (usage >= 0) {
-            VolumeItem volumeItem = new VolumeItem();
-            volumeItem.usage = usage;
-            volumeItem.rank = rank;
-            volumeItem.icon = item.getResourceId(R.styleable.carVolumeItems_item_icon, 0);
-            mVolumeItems.put(usage, volumeItem);
-            rank++;
-          }
-          item.recycle();
-        }
-      }
-    } catch (XmlPullParserException | IOException e) {
-      Log.e(TAG, "Error parsing volume groups configuration", e);
-    }
-  }
-
-  private VolumeItem getVolumeItemForUsages(int[] usages) {
-    int rank = Integer.MAX_VALUE;
-    VolumeItem result = null;
-    for (int usage : usages) {
-      VolumeItem volumeItem = mVolumeItems.get(usage);
-      if (volumeItem.rank < rank) {
-        rank = volumeItem.rank;
-        result = volumeItem;
-      }
-    }
-    return result;
-  }
-
-  private static int getSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-    try {
-      return carAudioManager.getGroupVolume(volumeGroupId);
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    return 0;
-  }
-
-  private static int getMaxSeekbarValue(CarAudioManager carAudioManager, int volumeGroupId) {
-    try {
-      return carAudioManager.getGroupMaxVolume(volumeGroupId);
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    return 0;
-  }
-
-  private SeekbarListItem addSeekbarListItem(VolumeItem volumeItem, int volumeGroupId,
-      int supplementalIconId, @Nullable View.OnClickListener supplementalIconOnClickListener) {
-    SeekbarListItem listItem = new SeekbarListItem(mContext);
-    listItem.setMax(getMaxSeekbarValue(mCarAudioManager, volumeGroupId));
-    int color = mContext.getResources().getColor(R.color.car_volume_dialog_tint);
-    int progress = getSeekbarValue(mCarAudioManager, volumeGroupId);
-    listItem.setProgress(progress);
-    listItem.setOnSeekBarChangeListener(
-        new CarVolumeDialogImpl.VolumeSeekBarChangeListener(volumeGroupId, mCarAudioManager));
-    Drawable primaryIcon = mContext.getResources().getDrawable(volumeItem.icon);
-    primaryIcon.mutate().setTint(color);
-    listItem.setPrimaryActionIcon(primaryIcon);
-    if (supplementalIconId != 0) {
-      Drawable supplementalIcon = mContext.getResources().getDrawable(supplementalIconId);
-      supplementalIcon.mutate().setTint(color);
-      listItem.setSupplementalIcon(supplementalIcon, true);
-      listItem.setSupplementalIconListener(supplementalIconOnClickListener);
-    } else {
-      listItem.setSupplementalEmptyIcon(true);
-      listItem.setSupplementalIconListener(null);
-    }
-
-    mVolumeLineItems.add(listItem);
-    volumeItem.listItem = listItem;
-    volumeItem.progress = progress;
-    return listItem;
-  }
-
-  private VolumeItem findVolumeItem(SeekbarListItem targetItem) {
-    for (int i = 0; i < mVolumeItems.size(); ++i) {
-      VolumeItem volumeItem = mVolumeItems.valueAt(i);
-      if (volumeItem.listItem == targetItem) {
-        return volumeItem;
-      }
-    }
-    return null;
-  }
-
-  private void cleanupAudioManager() {
-    try {
-      mCarAudioManager.unregisterVolumeCallback(mVolumeChangeCallback.asBinder());
-    } catch (CarNotConnectedException e) {
-      Log.e(TAG, "Car is not connected!", e);
-    }
-    mVolumeLineItems.clear();
-    mCarAudioManager = null;
-  }
-
-  private final class H extends Handler {
-    private static final int SHOW = 1;
-    private static final int DISMISS = 2;
-
-    public H() {
-      super(Looper.getMainLooper());
-    }
-
-    @Override
-    public void handleMessage(Message msg) {
-      switch (msg.what) {
-        case SHOW:
-          showH(msg.arg1);
-          break;
-        case DISMISS:
-          dismissH(msg.arg1);
-          break;
-        default:
-      }
-    }
-  }
-
-  private final class CustomDialog extends Dialog implements DialogInterface {
-    public CustomDialog(Context context) {
-      super(context, com.android.systemui.R.style.qs_theme);
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-      rescheduleTimeoutH();
-      return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    protected void onStart() {
-      super.setCanceledOnTouchOutside(true);
-      super.onStart();
-    }
-
-    @Override
-    protected void onStop() {
-      super.onStop();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-      if (isShowing()) {
-        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-          mHandler.obtainMessage(
-            H.DISMISS, Events.DISMISS_REASON_TOUCH_OUTSIDE).sendToTarget();
-          return true;
-        }
-      }
-      return false;
-    }
-  }
-
-  private final class ExpandIconListener implements View.OnClickListener {
-    @Override
-    public void onClick(final View v) {
-      mExpanded = !mExpanded;
-      Animator inAnimator;
-      if (mExpanded) {
-        for (int groupId = 0; groupId < mAvailableVolumeItems.size(); ++groupId) {
-          // Adding the items which are not coming from the default item.
-          VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-          if (volumeItem.defaultItem) {
-            // Set progress here due to the progress of seekbar may not be updated.
-            volumeItem.listItem.setProgress(volumeItem.progress);
-          } else {
-            addSeekbarListItem(volumeItem, groupId, 0, null);
-          }
-        }
-        inAnimator = AnimatorInflater.loadAnimator(
-            mContext, R.anim.car_arrow_fade_in_rotate_up);
-      } else {
-        // Only keeping the default stream if it is not expended.
-        Iterator itr = mVolumeLineItems.iterator();
-        while (itr.hasNext()) {
-          SeekbarListItem seekbarListItem = (SeekbarListItem) itr.next();
-          VolumeItem volumeItem = findVolumeItem(seekbarListItem);
-          if (!volumeItem.defaultItem) {
-            itr.remove();
-          } else {
-            // Set progress here due to the progress of seekbar may not be updated.
-            seekbarListItem.setProgress(volumeItem.progress);
-          }
-        }
-        inAnimator = AnimatorInflater.loadAnimator(
-            mContext, R.anim.car_arrow_fade_in_rotate_down);
-      }
-
-      Animator outAnimator = AnimatorInflater.loadAnimator(
-          mContext, R.anim.car_arrow_fade_out);
-      inAnimator.setStartDelay(ARROW_FADE_IN_START_DELAY_IN_MILLIS);
-      AnimatorSet animators = new AnimatorSet();
-      animators.playTogether(outAnimator, inAnimator);
-      animators.setTarget(v);
-      animators.start();
-      mPagedListAdapter.notifyDataSetChanged();
-    }
-  }
-
-  private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
-    private final int mVolumeGroupId;
-    private final CarAudioManager mCarAudioManager;
-
-    private VolumeSeekBarChangeListener(int volumeGroupId, CarAudioManager carAudioManager) {
-      mVolumeGroupId = volumeGroupId;
-      mCarAudioManager = carAudioManager;
-    }
-
-    @Override
-    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-      if (!fromUser) {
-        // For instance, if this event is originated from AudioService,
-        // we can ignore it as it has already been handled and doesn't need to be
-        // sent back down again.
-        return;
-      }
-      try {
-        if (mCarAudioManager == null) {
-          Log.w(TAG, "Ignoring volume change event because the car isn't connected");
-          return;
-        }
-        mAvailableVolumeItems.get(mVolumeGroupId).progress = progress;
-        mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0);
-      } catch (CarNotConnectedException e) {
-        Log.e(TAG, "Car is not connected!", e);
-      }
-    }
-
-    @Override
-    public void onStartTrackingTouch(SeekBar seekBar) {}
-
-    @Override
-    public void onStopTrackingTouch(SeekBar seekBar) {}
-  }
-
-  private final ICarVolumeCallback mVolumeChangeCallback = new ICarVolumeCallback.Stub() {
-    @Override
-    public void onGroupVolumeChanged(int groupId, int flags) {
-      VolumeItem volumeItem = mAvailableVolumeItems.get(groupId);
-      int value = getSeekbarValue(mCarAudioManager, groupId);
-      // Do not update the progress if it is the same as before. When car audio manager sets its
-      // group volume caused by the seekbar progress changed, it also triggers this callback.
-      // Updating the seekbar at the same time could block the continuous seeking.
-      if (value != volumeItem.progress) {
-        volumeItem.listItem.setProgress(value);
-        volumeItem.progress = value;
-      }
-      if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
-        show(Events.SHOW_REASON_VOLUME_CHANGED);
-      }
-    }
-
-    @Override
-    public void onMasterMuteChanged(int flags) {
-      // ignored
-    }
-  };
-
-  private final ServiceConnection mServiceConnection = new ServiceConnection() {
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-      try {
-        mExpanded = false;
-        mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
-        int volumeGroupCount = mCarAudioManager.getVolumeGroupCount();
-        // Populates volume slider items from volume groups to UI.
-        for (int groupId = 0; groupId < volumeGroupCount; groupId++) {
-          VolumeItem volumeItem = getVolumeItemForUsages(
-              mCarAudioManager.getUsagesForVolumeGroupId(groupId));
-          mAvailableVolumeItems.add(volumeItem);
-          // The first one is the default item.
-          if (groupId == 0) {
-            volumeItem.defaultItem = true;
-            addSeekbarListItem(volumeItem, groupId, R.drawable.car_ic_keyboard_arrow_down,
-                new ExpandIconListener());
-          }
-        }
-
-        // If list is already initiated, update its content.
-        if (mPagedListAdapter != null) {
-          mPagedListAdapter.notifyDataSetChanged();
-        }
-        mCarAudioManager.registerVolumeCallback(mVolumeChangeCallback.asBinder());
-      } catch (CarNotConnectedException e) {
-        Log.e(TAG, "Car is not connected!", e);
-      }
-    }
-
-    /**
-     * This does not get called when service is properly disconnected.
-     * So we need to also handle cleanups in destroy().
-     */
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-      cleanupAudioManager();
-    }
-  };
-
-  /**
-   * Wrapper class which contains information of each volume group.
-   */
-  private static class VolumeItem {
-    private @AudioAttributes.AttributeUsage int usage;
-    private int rank;
-    private boolean defaultItem = false;
-    private @DrawableRes int icon;
-    private SeekbarListItem listItem;
-    private int progress;
-  }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 2861dff..0805677 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -19,12 +19,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
-import android.os.Handler;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
@@ -57,7 +55,7 @@
     public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
 
     private final SystemUI mSysui;
-    private final Context mContext;
+    protected final Context mContext;
     private final VolumeDialogControllerImpl mController;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
@@ -70,7 +68,7 @@
             400    // vibrateToSilentDebounce
     );
 
-    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
+    public VolumeDialogComponent(SystemUI sysui, Context context) {
         mSysui = sysui;
         mContext = context;
         mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
@@ -81,7 +79,6 @@
         Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
                 .withDefault(this::createDefault)
-                .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
                 .withCallback(dialog -> {
                     if (mDialog != null) {
                         mDialog.destroy();
@@ -94,7 +91,7 @@
                 VOLUME_SILENT_DO_NOT_DISTURB);
     }
 
-    private VolumeDialog createDefault() {
+    protected VolumeDialog createDefault() {
         VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
@@ -102,10 +99,6 @@
         return impl;
     }
 
-    private VolumeDialog createCarDefault() {
-        return new CarVolumeDialogImpl(mContext);
-    }
-
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (VOLUME_DOWN_SILENT.equals(key)) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f37de..f8cf793 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -22,6 +22,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIFactory;
 import com.android.systemui.qs.tiles.DndTile;
 
 import java.io.FileDescriptor;
@@ -43,7 +44,9 @@
             mContext.getResources().getBoolean(R.bool.enable_safety_warning);
         mEnabled = enableVolumeUi || enableSafetyWarning;
         if (!mEnabled) return;
-        mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
+
+        mVolumeComponent = SystemUIFactory.getInstance()
+                .createVolumeDialogComponent(this, mContext);
         mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
         putComponent(VolumeComponent.class, getVolumeComponent());
         setDefaultVolumeController();
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index b32bf99..83ec33c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -38,8 +38,6 @@
     android.test.runner \
     telephony-common \
     android.test.base \
-    android.car \
-    android.car.userlib
 
 LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index 6ac4462..ec2319d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -16,9 +16,8 @@
 
 package com.android.systemui.doze;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,8 +26,8 @@
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.DozeParameters;
 
 import org.junit.Before;
@@ -59,14 +58,14 @@
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
-        verify(mIWallpaperManager).setInAmbientMode(eq(true), anyBoolean());
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), anyLong());
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
-        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
 
         // Make sure we're sending false when AoD is off
         reset(mDozeParameters);
         mDozeWallpaperState.transitionTo(DozeMachine.State.FINISH, DozeMachine.State.DOZE_AOD);
-        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyBoolean());
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), anyLong());
     }
 
     @Test
@@ -78,10 +77,12 @@
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
-        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(true));
+        verify(mIWallpaperManager).setInAmbientMode(eq(true),
+                eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
-        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+        verify(mIWallpaperManager).setInAmbientMode(eq(false),
+                eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
     }
 
     @Test
@@ -93,24 +94,24 @@
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED,
                 DozeMachine.State.DOZE_AOD);
-        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
 
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH);
-        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(false));
+        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L));
     }
 
     @Test
     public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException {
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE,
                 DozeMachine.State.DOZE_REQUEST_PULSE);
-        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
     }
 
     @Test
     public void testTransitionTo_pulseIsAmbientMode() throws RemoteException {
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE,
                 DozeMachine.State.DOZE_PULSING);
-        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(false));
+        verify(mIWallpaperManager).setInAmbientMode(eq(true), eq(0L));
     }
 
     @Test
@@ -120,6 +121,7 @@
         reset(mIWallpaperManager);
         mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_PULSING,
                 DozeMachine.State.FINISH);
-        verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(true));
+        verify(mIWallpaperManager).setInAmbientMode(eq(false),
+                eq((long) StackStateAnimator.ANIMATION_DURATION_WAKEUP));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
deleted file mode 100644
index f89a932..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/car/CarQsFragmentTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.qs.car;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.mock;
-
-import android.content.Context;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.LayoutInflaterBuilder;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.keyguard.CarrierText;
-import com.android.systemui.Dependency;
-import com.android.systemui.SysuiBaseFragmentTest;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.Clock;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link CarQSFragment}.
- */
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
-@SmallTest
-@Ignore
-public class CarQsFragmentTest extends SysuiBaseFragmentTest {
-    public CarQsFragmentTest() {
-        super(CarQSFragment.class);
-    }
-
-    @Before
-    public void initDependencies() {
-        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
-                new LayoutInflaterBuilder(mContext)
-                        .replace("com.android.systemui.statusbar.policy.SplitClockView",
-                                FrameLayout.class)
-                        .replace("TextClock", View.class)
-                        .replace(CarrierText.class, View.class)
-                        .replace(Clock.class, View.class)
-                        .build());
-        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mDependency.injectTestDependency(Dependency.BG_LOOPER,
-                TestableLooper.get(this).getLooper());
-    }
-
-    @Test
-    @Ignore("Flaky")
-    public void testLayoutInflation() {
-        CarQSFragment fragment = (CarQSFragment) mFragment;
-        mFragments.dispatchResume();
-
-        assertNotNull(fragment.getHeader());
-        assertNotNull(fragment.getFooter());
-    }
-
-    @Test
-    @Ignore("Flaky")
-    public void testListening() {
-        CarQSFragment qs = (CarQSFragment) mFragment;
-        mFragments.dispatchResume();
-        processAllMessages();
-
-        qs.setListening(true);
-        processAllMessages();
-
-        qs.setListening(false);
-        processAllMessages();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f63d236..26fa20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -14,41 +14,76 @@
 
 package com.android.systemui.qs.customize;
 
-import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.content.pm.PackageManager;
+import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.systemui.Dependency;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSTileHost;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class TileQueryHelperTest extends SysuiTestCase {
-    @Mock private TileQueryHelper.TileStateListener mListener;
-    @Mock private QSTileHost mQSTileHost;
+    private static final String CURRENT_TILES = "wifi,dnd,nfc";
+    private static final String ONLY_STOCK_TILES = "wifi,dnd";
+    private static final String WITH_OTHER_TILES = "wifi,dnd,other";
+    // Note no nfc in stock tiles
+    private static final String STOCK_TILES = "wifi,dnd,cell,battery";
+    private static final String ALL_TILES = "wifi,dnd,nfc,cell,battery";
+    private static final Set<String> FACTORY_TILES = new ArraySet<>();
 
+    static {
+        FACTORY_TILES.addAll(Arrays.asList(
+                new String[]{"wifi", "bt", "cell", "dnd", "inversion", "airplane", "work",
+                        "rotation", "flashlight", "location", "cast", "hotspot", "user", "battery",
+                        "saver", "night", "nfc"}));
+    }
+
+    @Mock
+    private TileQueryHelper.TileStateListener mListener;
+    @Mock
+    private QSTileHost mQSTileHost;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private QSTile.State mState;
     private TestableLooper mBGLooper;
-
     private TileQueryHelper mTileQueryHelper;
 
     @Before
@@ -56,6 +91,23 @@
         MockitoAnnotations.initMocks(this);
         mBGLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+        mContext.setMockPackageManager(mPackageManager);
+
+        mState = new QSTile.State();
+        doAnswer(invocation -> {
+                    String spec = (String) invocation.getArguments()[0];
+                    if (FACTORY_TILES.contains(spec)) {
+                        QSTile m = mock(QSTile.class);
+                        when(m.isAvailable()).thenReturn(true);
+                        when(m.getTileSpec()).thenReturn(spec);
+                        when(m.getState()).thenReturn(mState);
+                        return m;
+                    } else {
+                        return null;
+                    }
+                }
+        ).when(mQSTileHost).createTile(anyString());
+
         mTileQueryHelper = new TileQueryHelper(mContext, mListener);
     }
 
@@ -98,4 +150,72 @@
 
         assertTrue(mTileQueryHelper.isFinished());
     }
+
+    @Test
+    public void testQueryTiles_correctTilesAndOrderOnlyStockTiles() {
+        ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+                ONLY_STOCK_TILES);
+        mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+                STOCK_TILES);
+
+        mTileQueryHelper.queryTiles(mQSTileHost);
+
+        mBGLooper.processAllMessages();
+        waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+        verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+        List<String> specs = new ArrayList<>();
+        for (TileQueryHelper.TileInfo t : captor.getValue()) {
+            specs.add(t.spec);
+        }
+        String tiles = TextUtils.join(",", specs);
+        assertThat(tiles, is(equalTo(STOCK_TILES)));
+    }
+
+    @Test
+    public void testQueryTiles_correctTilesAndOrderOtherFactoryTiles() {
+        ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+                CURRENT_TILES);
+        mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+                STOCK_TILES);
+
+        mTileQueryHelper.queryTiles(mQSTileHost);
+
+        mBGLooper.processAllMessages();
+        waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+        verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+        List<String> specs = new ArrayList<>();
+        for (TileQueryHelper.TileInfo t : captor.getValue()) {
+            specs.add(t.spec);
+        }
+        String tiles = TextUtils.join(",", specs);
+        assertThat(tiles, is(equalTo(ALL_TILES)));
+    }
+
+    @Test
+    public void testQueryTiles_otherTileNotIncluded() {
+        ArgumentCaptor<List<TileQueryHelper.TileInfo>> captor = ArgumentCaptor.forClass(List.class);
+
+        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.QS_TILES,
+                WITH_OTHER_TILES);
+        mContext.getOrCreateTestableResources().addOverride(R.string.quick_settings_tiles_stock,
+                STOCK_TILES);
+
+        mTileQueryHelper.queryTiles(mQSTileHost);
+
+        mBGLooper.processAllMessages();
+        waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER));
+
+        verify(mListener, atLeastOnce()).onTilesChanged(captor.capture());
+        List<String> specs = new ArrayList<>();
+        for (TileQueryHelper.TileInfo t : captor.getValue()) {
+            specs.add(t.spec);
+        }
+        assertFalse(specs.contains("other"));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f49c5b4..9d0556f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -120,7 +120,7 @@
         mTestHandler = Handler.createAsync(Looper.myLooper());
         mSbn = createNewNotification(0 /* id */);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mAlertingNotificationManager = createAlertingNotificationManager();
     }
@@ -171,7 +171,7 @@
         for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
             StatusBarNotification sbn = createNewNotification(i);
             NotificationData.Entry entry = new NotificationData.Entry(sbn);
-            entry.row = mRow;
+            entry.setRow(mRow);
             mAlertingNotificationManager.showNotification(entry);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 8ae3cd8..a4fe52d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.statusbar;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -64,11 +66,12 @@
         verify(mCallbacks).removeIcon(eq(slot));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testDisable() {
         int state1 = 14;
         int state2 = 42;
-        mCommandQueue.disable(state1, state2);
+        mCommandQueue.disable(DEFAULT_DISPLAY, state1, state2);
         waitForIdleSync();
         verify(mCallbacks).disable(eq(state1), eq(state2), eq(true));
     }
@@ -95,24 +98,27 @@
         verify(mCallbacks).animateExpandSettingsPanel(eq(panel));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testSetSystemUiVisibility() {
         Rect r = new Rect();
-        mCommandQueue.setSystemUiVisibility(1, 2, 3, 4, null, r);
+        mCommandQueue.setSystemUiVisibility(DEFAULT_DISPLAY, 1, 2, 3, 4, null, r);
         waitForIdleSync();
         verify(mCallbacks).setSystemUiVisibility(eq(1), eq(2), eq(3), eq(4), eq(null), eq(r));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testTopAppWindowChanged() {
-        mCommandQueue.topAppWindowChanged(true);
+        mCommandQueue.topAppWindowChanged(DEFAULT_DISPLAY, true);
         waitForIdleSync();
         verify(mCallbacks).topAppWindowChanged(eq(true));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(null, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, 1, 2, true);
         waitForIdleSync();
         verify(mCallbacks).setImeWindowStatus(eq(null), eq(1), eq(2), eq(true));
     }
@@ -166,9 +172,10 @@
         verify(mCallbacks).toggleKeyboardShortcutsMenu(eq(1));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testSetWindowState() {
-        mCommandQueue.setWindowState(1, 2);
+        mCommandQueue.setWindowState(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
         verify(mCallbacks).setWindowState(eq(1), eq(2));
     }
@@ -180,30 +187,34 @@
         verify(mCallbacks).showScreenPinningRequest(eq(1));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionPending() {
-        mCommandQueue.appTransitionPending();
+        mCommandQueue.appTransitionPending(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionPending(eq(false));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionCancelled() {
-        mCommandQueue.appTransitionCancelled();
+        mCommandQueue.appTransitionCancelled(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionCancelled();
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionStarting() {
-        mCommandQueue.appTransitionStarting(1, 2);
+        mCommandQueue.appTransitionStarting(DEFAULT_DISPLAY, 1, 2);
         waitForIdleSync();
         verify(mCallbacks).appTransitionStarting(eq(1L), eq(2L), eq(false));
     }
 
+    // TODO(b/117478341): add test case for multi-display
     @Test
     public void testAppTransitionFinished() {
-        mCommandQueue.appTransitionFinished();
+        mCommandQueue.appTransitionFinished(DEFAULT_DISPLAY);
         waitForIdleSync();
         verify(mCallbacks).appTransitionFinished();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d409e2b..b5d305d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -78,7 +78,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mCallback,
                 mDelegate, mController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index aca1f90..9bed59b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -227,7 +227,7 @@
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
-        entry.row = row;
+        entry.setRow(row);
         entry.createIcons(mContext, sbn);
         entry.channel = new NotificationChannel(
                 notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 602e613..72d6cd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -37,6 +37,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -73,7 +74,7 @@
     @Mock private ShadeController mShadeController;
 
     private NotificationViewHierarchyManager mViewHierarchyManager;
-    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);;
+    private NotificationTestHelper mHelper = new NotificationTestHelper(mContext);
 
     @Before
     public void setUp() {
@@ -95,7 +96,7 @@
     private NotificationData.Entry createEntry() throws Exception {
         ExpandableNotificationRow row = mHelper.createRow();
         NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
-        entry.row = row;
+        entry.setRow(row);
         return entry;
     }
 
@@ -108,9 +109,9 @@
         NotificationData.Entry entry2 = createEntry();
 
         // Set up the prior state to look like three top level notifications.
-        mListContainer.addContainerView(entry0.row);
-        mListContainer.addContainerView(entry1.row);
-        mListContainer.addContainerView(entry2.row);
+        mListContainer.addContainerView(entry0.getRow());
+        mListContainer.addContainerView(entry1.getRow());
+        mListContainer.addContainerView(entry2.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -118,15 +119,15 @@
         when(mGroupManager.isChildInGroupWithSummary(entry0.notification)).thenReturn(false);
         when(mGroupManager.isChildInGroupWithSummary(entry1.notification)).thenReturn(true);
         when(mGroupManager.isChildInGroupWithSummary(entry2.notification)).thenReturn(true);
-        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0.row);
-        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0.row);
+        when(mGroupManager.getGroupSummary(entry1.notification)).thenReturn(entry0);
+        when(mGroupManager.getGroupSummary(entry2.notification)).thenReturn(entry0);
 
         // Run updateNotifications - the view hierarchy should be reorganized.
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(mListContainer).notifyGroupChildAdded(entry1.row);
-        verify(mListContainer).notifyGroupChildAdded(entry2.row);
-        assertTrue(Lists.newArrayList(entry0.row).equals(mListContainer.mRows));
+        verify(mListContainer).notifyGroupChildAdded(entry1.getRow());
+        verify(mListContainer).notifyGroupChildAdded(entry2.getRow());
+        assertTrue(Lists.newArrayList(entry0.getRow()).equals(mListContainer.mRows));
     }
 
     @Test
@@ -135,11 +136,11 @@
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
         NotificationData.Entry entry2 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
-        entry0.row.addChildNotification(entry2.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
+        entry0.getRow().addChildNotification(entry2.getRow());
 
         // Set up the prior state to look like one top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1, entry2));
 
@@ -152,10 +153,12 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
         verify(mListContainer).notifyGroupChildRemoved(
-                entry2.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row, entry2.row).equals(mListContainer.mRows));
+                entry2.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(
+                Lists.newArrayList(entry0.getRow(), entry1.getRow(), entry2.getRow())
+                        .equals(mListContainer.mRows));
     }
 
     @Test
@@ -163,10 +166,10 @@
         // Tests two top level notifications becoming a suppressed summary and a child.
         NotificationData.Entry entry0 = createEntry();
         NotificationData.Entry entry1 = createEntry();
-        entry0.row.addChildNotification(entry1.row);
+        entry0.getRow().addChildNotification(entry1.getRow());
 
         // Set up the prior state to look like a top level notification.
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0, entry1));
 
@@ -179,23 +182,23 @@
         mViewHierarchyManager.updateNotificationViews();
 
         verify(mListContainer).notifyGroupChildRemoved(
-                entry1.row, entry0.row.getChildrenContainer());
-        assertTrue(Lists.newArrayList(entry0.row, entry1.row).equals(mListContainer.mRows));
-        assertEquals(View.GONE, entry0.row.getVisibility());
-        assertEquals(View.VISIBLE, entry1.row.getVisibility());
+                entry1.getRow(), entry0.getRow().getChildrenContainer());
+        assertTrue(Lists.newArrayList(entry0.getRow(), entry1.getRow()).equals(mListContainer.mRows));
+        assertEquals(View.GONE, entry0.getRow().getVisibility());
+        assertEquals(View.VISIBLE, entry1.getRow().getVisibility());
     }
 
     @Test
     public void testUpdateNotificationViews_appOps() throws Exception {
         NotificationData.Entry entry0 = createEntry();
-        entry0.row = spy(entry0.row);
+        entry0.setRow(spy(entry0.getRow()));
         when(mNotificationData.getActiveNotifications()).thenReturn(
                 Lists.newArrayList(entry0));
-        mListContainer.addContainerView(entry0.row);
+        mListContainer.addContainerView(entry0.getRow());
 
         mViewHierarchyManager.updateNotificationViews();
 
-        verify(entry0.row, times(1)).showAppOpsIcons(any());
+        verify(entry0.getRow(), times(1)).showAppOpsIcons(any());
     }
 
     private class FakeListContainer implements NotificationListContainer {
@@ -206,19 +209,19 @@
         public void setChildTransferInProgress(boolean childTransferInProgress) {}
 
         @Override
-        public void changeViewPosition(View child, int newIndex) {
+        public void changeViewPosition(ExpandableView child, int newIndex) {
             mRows.remove(child);
             mRows.add(newIndex, child);
         }
 
         @Override
-        public void notifyGroupChildAdded(View row) {}
+        public void notifyGroupChildAdded(ExpandableView row) {}
 
         @Override
-        public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {}
+        public void notifyGroupChildRemoved(ExpandableView row, ViewGroup childrenContainer) {}
 
         @Override
-        public void generateAddAnimation(View child, boolean fromMoreCard) {}
+        public void generateAddAnimation(ExpandableView child, boolean fromMoreCard) {}
 
         @Override
         public void generateChildOrderChangedEvent() {}
@@ -252,7 +255,7 @@
         public void setMaxDisplayedNotifications(int maxNotifications) {}
 
         @Override
-        public void snapViewIfNeeded(ExpandableNotificationRow row) {}
+        public void snapViewIfNeeded(Entry entry) {}
 
         @Override
         public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
@@ -271,10 +274,10 @@
         }
 
         @Override
-        public void cleanUpViewState(View view) {}
+        public void cleanUpViewStateForEntry(Entry entry) { }
 
         @Override
-        public boolean isInVisibleLocation(ExpandableNotificationRow row) {
+        public boolean isInVisibleLocation(Entry entry) {
             return true;
         }
 
@@ -286,5 +289,6 @@
         public boolean hasPulsingNotifications() {
             return false;
         }
+
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 435ede4..5558393 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -62,6 +62,7 @@
 
     @Before
     public void setUp() throws Exception {
+        when(mStatusBarWindowView.getResources()).thenReturn(mContext.getResources());
         when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
         mLaunchAnimator = new ActivityLaunchAnimator(
                 mStatusBarWindowView,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index b3b45eb..f94ba95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -121,9 +121,9 @@
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
         mNotificationData = new TestableNotificationData();
-        Dependency.get(InitController.class).executePostInitTasks();
         mNotificationData.updateRanking(mock(NotificationListenerService.RankingMap.class));
         mRow = new NotificationTestHelper(getContext()).createRow();
+        Dependency.get(InitController.class).executePostInitTasks();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index d1fe5af..8706e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -262,14 +262,14 @@
                 NotificationData.Entry.class);
         verify(mCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
         NotificationData.Entry entry = entryCaptor.getValue();
-        verify(mRemoteInputManager).bindRow(entry.row);
+        verify(mRemoteInputManager).bindRow(entry.getRow());
 
         // Row content inflation:
         verify(mCallback).onNotificationAdded(entry);
         verify(mPresenter).updateNotificationViews();
 
         assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
-        assertNotNull(entry.row);
+        assertNotNull(entry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
     }
@@ -294,7 +294,7 @@
         verify(mPresenter).updateNotificationViews();
         verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
         verify(mCallback).onNotificationUpdated(mSbn);
-        assertNotNull(mEntry.row);
+        assertNotNull(mEntry.getRow());
         assertEquals(mEntry.userSentiment,
                 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
     }
@@ -303,7 +303,7 @@
     public void testRemoveNotification() throws Exception {
         com.android.systemui.util.Assert.isNotMainThread();
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -313,7 +313,7 @@
 
         verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
         verify(mForegroundServiceController).removeNotification(mSbn);
-        verify(mListContainer).cleanUpViewState(mRow);
+        verify(mListContainer).cleanUpViewStateForEntry(mEntry);
         verify(mPresenter).updateNotificationViews();
         verify(mCallback).onNotificationRemoved(mSbn.getKey(), mSbn);
         verify(mRow).setRemoved();
@@ -332,7 +332,7 @@
         extenders.clear();
         extenders.add(extender);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
@@ -347,7 +347,7 @@
 
         when(mForegroundServiceController.getStandardLayoutKey(anyInt(), anyString()))
                 .thenReturn(mEntry.key);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
 
         mEntryManager.updateNotificationsForAppOp(
@@ -372,7 +372,7 @@
 
     @Test
     public void testAddNotificationExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> expected = new ArraySet<>();
         expected.add(3);
@@ -395,7 +395,7 @@
 
     @Test
     public void testAdd_noExistingAppOps() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         when(mForegroundServiceController.getStandardLayoutKey(
                 mEntry.notification.getUserId(),
@@ -409,7 +409,7 @@
 
     @Test
     public void testAdd_existingAppOpsNotForegroundNoti() {
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(3);
@@ -431,7 +431,7 @@
         when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntry.setInflationTask(mAsyncInflationTask);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
@@ -447,7 +447,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, null);
 
@@ -461,7 +461,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.getNotificationData().add(mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
@@ -476,7 +476,7 @@
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
         when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
 
-        mEntry.row = null;
+        mEntry.setRow(null);
         mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
         setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index ffb1c2d..f190979 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -47,7 +47,7 @@
     public void setUp() {
         mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
         mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1c7a8e8..3710fa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -89,7 +89,7 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
                 0, new Notification(), UserHandle.CURRENT, null, 0);
         mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
+        mEntry.setRow(mRow);
 
         mLogger = new TestableNotificationLogger(mBarService);
         mLogger.setUpWithContainer(mListContainer);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 7fee0ee..f0fa788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 3d2ea70..766c5d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -20,8 +20,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -62,8 +61,7 @@
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager
-        .OnSettingsClickListener;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -177,10 +175,17 @@
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
 
         ExpandableNotificationRow row = spy(realRow);
+
         when(row.getWindowToken()).thenReturn(new Binder());
         when(row.getGuts()).thenReturn(guts);
         doNothing().when(row).inflateGuts();
 
+        NotificationData.Entry realEntry = realRow.getEntry();
+        NotificationData.Entry entry = spy(realEntry);
+
+        when(entry.getRow()).thenReturn(row);
+        when(entry.getGuts()).thenReturn(guts);
+
         mGutsManager.openGuts(row, 0, 0, menuItem);
         mTestableLooper.processAllMessages();
         verify(guts).openControls(
@@ -190,13 +195,19 @@
                 anyBoolean(),
                 any(Runnable.class));
 
+        // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
+        verify(row).setGutsView(any());
+
         row.onDensityOrFontScaleChanged();
-        mGutsManager.onDensityOrFontScaleChanged(row);
+        mGutsManager.onDensityOrFontScaleChanged(entry);
+
         mTestableLooper.processAllMessages();
 
         mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
 
         verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+
+        // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
         verify(row, times(2)).setGutsView(any());
     }
 
@@ -284,8 +295,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -301,9 +311,7 @@
                 eq(false),
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
-                eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -315,8 +323,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -332,41 +339,7 @@
                 eq(false),
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
-                eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
-    }
-
-    @Test
-    public void testInitializeNotificationInfoView_noisy() throws Exception {
-        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
-        ExpandableNotificationRow row = spy(mHelper.createRow());
-        row.setBlockingHelperShowing(true);
-        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
-        row.getEntry().noisy = true;
-        when(row.getIsNonblockable()).thenReturn(false);
-        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
-
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
-
-        verify(notificationInfoView).bindNotification(
-                any(PackageManager.class),
-                any(INotificationManager.class),
-                eq(statusBarNotification.getPackageName()),
-                any(NotificationChannel.class),
-                anyInt(),
-                eq(statusBarNotification),
-                any(NotificationInfo.CheckSaveListener.class),
-                any(NotificationInfo.OnSettingsClickListener.class),
-                any(NotificationInfo.OnAppSettingsClickListener.class),
-                eq(false),
-                eq(false),
-                eq(true) /* isForBlockingHelper */,
-                eq(true) /* isUserSentimentNegative */,
-                eq(true) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -379,8 +352,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -396,9 +368,7 @@
                 eq(false),
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
-                eq(false) /*isNoisy */,
-                eq(IMPORTANCE_DEFAULT),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(IMPORTANCE_DEFAULT));
     }
 
     @Test
@@ -411,8 +381,7 @@
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_NONE);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -428,9 +397,7 @@
                 eq(false),
                 eq(false) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
-                eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_NONE));
+                eq(0));
     }
 
     @Test
@@ -442,8 +409,7 @@
         when(row.getIsNonblockable()).thenReturn(false);
         StatusBarNotification statusBarNotification = row.getStatusBarNotification();
 
-        mGutsManager.initializeNotificationInfo(row, notificationInfoView,
-                NotificationInfo.ACTION_BLOCK);
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
 
         verify(notificationInfoView).bindNotification(
                 any(PackageManager.class),
@@ -459,9 +425,7 @@
                 eq(false),
                 eq(true) /* isForBlockingHelper */,
                 eq(true) /* isUserSentimentNegative */,
-                eq(false) /*isNoisy */,
-                eq(0),
-                eq(NotificationInfo.ACTION_BLOCK));
+                eq(0));
     }
 
     @Test
@@ -470,7 +434,7 @@
         ExpandableNotificationRow row = spy(createTestNotificationRow());
         doReturn(guts).when(row).getGuts();
         NotificationData.Entry entry = row.getEntry();
-        entry.row = row;
+        entry.setRow(row);
         mGutsManager.setExposedGuts(guts);
 
         assertTrue(mGutsManager.shouldExtendLifetime(entry));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 985827a..d28f017 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -50,12 +50,9 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
 import android.os.UserHandle;
@@ -86,8 +83,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
@@ -187,7 +182,7 @@
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -200,7 +195,7 @@
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -209,7 +204,7 @@
     public void testBindNotification_noDelegate() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
         final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -228,7 +223,7 @@
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Other"));
@@ -240,7 +235,7 @@
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
         final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -257,7 +252,7 @@
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -269,7 +264,7 @@
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -278,7 +273,7 @@
     public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
@@ -291,7 +286,7 @@
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true,
-                false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                false, IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -300,7 +295,7 @@
     public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -309,62 +304,104 @@
     public void testBindNotification_BlockButton() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-               false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final View block = mNotificationInfo.findViewById(R.id.block);
-        final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
-        final View minimize = mNotificationInfo.findViewById(R.id.minimize);
+               IMPORTANCE_DEFAULT);
+        final View block = mNotificationInfo.findViewById(R.id.int_block);
+        final View minimize = mNotificationInfo.findViewById(R.id.block_or_minimize);
         assertEquals(VISIBLE, block.getVisibility());
-        assertEquals(GONE, toggleSilent.getVisibility());
         assertEquals(GONE, minimize.getVisibility());
     }
 
     @Test
-    public void testBindNotification_SilenceButton() throws Exception {
+    public void testBindNotification_BlockButton_BlockHelper() throws Exception {
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                true /* isBlockingHelper */, false, IMPORTANCE_DEFAULT);
+        final View block = mNotificationInfo.findViewById(R.id.block);
+        final View interruptivenessSettings = mNotificationInfo.findViewById(
+                R.id.interruptiveness_settings);
+        assertEquals(VISIBLE, block.getVisibility());
+        assertEquals(GONE, interruptivenessSettings.getVisibility());
+    }
+
+    @Test
+    public void testBindNotification_SilenceButton_CurrentlyAlerting() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
-        assertEquals(VISIBLE, toggleSilent.getVisibility());
+                IMPORTANCE_DEFAULT);
+        final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+        assertEquals(VISIBLE, silent.getVisibility());
         assertEquals(
-                mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+                mContext.getString(R.string.inline_silent_button_silent), silent.getText());
     }
 
     @Test
-    public void testBindNotification_UnSilenceButton() throws Exception {
+    public void testBindNotification_SilenceButton_CurrentlySilent() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
-        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
-        assertEquals(VISIBLE, toggleSilent.getVisibility());
+                IMPORTANCE_LOW);
+        final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+        assertEquals(VISIBLE, silent.getVisibility());
         assertEquals(
-                mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+                mContext.getString(R.string.inline_silent_button_stay_silent),
+                silent.getText());
     }
 
     @Test
-    public void testBindNotification_SilenceButton_ChannelImportanceUnspecified() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+    public void testBindNotification_AlertButton_CurrentlySilent() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
-        assertEquals(VISIBLE, toggleSilent.getVisibility());
+                IMPORTANCE_LOW);
+        final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+        assertEquals(VISIBLE, alert.getVisibility());
         assertEquals(
-                mContext.getString(R.string.inline_silent_button_silent), toggleSilent.getText());
+                mContext.getString(R.string.inline_silent_button_alert), alert.getText());
     }
 
     @Test
-    public void testBindNotification_UnSilenceButton_ChannelImportanceUnspecified()
-            throws Exception {
+    public void testBindNotification_UnSilenceButton_currentlyAlerting() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
+        final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+        assertEquals(VISIBLE, alert.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+    }
+
+    @Test
+    public void testBindNotification_ChannelImportanceUnspecified_NotifAlerting() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
-        final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent);
-        assertEquals(VISIBLE, toggleSilent.getVisibility());
+                IMPORTANCE_DEFAULT);
+        final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+        final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+        assertEquals(VISIBLE, silent.getVisibility());
+        assertEquals(VISIBLE, alert.getVisibility());
         assertEquals(
-                mContext.getString(R.string.inline_silent_button_alert), toggleSilent.getText());
+                mContext.getString(R.string.inline_silent_button_silent), silent.getText());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_keep_alerting), alert.getText());
+    }
+
+    @Test
+    public void testBindNotification_ChannelImportanceUnspecified_NotifSilent() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_LOW);
+        final TextView silent = mNotificationInfo.findViewById(R.id.int_silent);
+        final TextView alert = mNotificationInfo.findViewById(R.id.int_alert);
+        assertEquals(VISIBLE, silent.getVisibility());
+        assertEquals(VISIBLE, alert.getVisibility());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_stay_silent), silent.getText());
+        assertEquals(
+                mContext.getString(R.string.inline_silent_button_alert), alert.getText());
     }
 
     @Test
@@ -372,10 +409,13 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final View block = mNotificationInfo.findViewById(R.id.block);
+        final View interruptivenessSettings = mNotificationInfo.findViewById(
+                R.id.interruptiveness_settings);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(GONE, block.getVisibility());
+        assertEquals(GONE, interruptivenessSettings.getVisibility());
         assertEquals(VISIBLE, minimize.getVisibility());
     }
 
@@ -387,7 +427,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
                     latch.countDown();
-                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, false, IMPORTANCE_DEFAULT);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -399,7 +439,7 @@
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -411,7 +451,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
-                }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, false, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -420,11 +460,11 @@
     public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
-                }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, false, IMPORTANCE_DEFAULT);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -433,7 +473,7 @@
     public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(0)).count(anyString(), anyInt());
     }
@@ -442,7 +482,7 @@
     public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true, IMPORTANCE_DEFAULT);
         mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
         verify(mMetricsLogger, times(1)).count(anyString(), anyInt());
     }
@@ -455,7 +495,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(null, c);
                     latch.countDown();
-                }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, true, true, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -468,7 +508,7 @@
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                null, true, true, IMPORTANCE_DEFAULT);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -479,7 +519,7 @@
     public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
-                null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                null, true, true, IMPORTANCE_DEFAULT);
         final TextView blockView = mNotificationInfo.findViewById(R.id.block);
         assertEquals(GONE, blockView.getVisibility());
     }
@@ -488,7 +528,7 @@
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true, IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -498,7 +538,7 @@
     public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -509,7 +549,7 @@
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -520,9 +560,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -534,7 +574,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         mTestableLooper.processAllMessages();
@@ -548,9 +588,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_silent).performClick();
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -562,9 +602,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_alert).performClick();
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
@@ -576,7 +616,7 @@
         int originalImportance = mNotificationChannel.getImportance();
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
         mTestableLooper.processAllMessages();
@@ -591,7 +631,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -609,10 +649,10 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
-                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+        );
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -631,10 +671,10 @@
                 TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
                 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
-                true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true, false /* isNonblockable */, IMPORTANCE_DEFAULT
+        );
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -653,8 +693,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -682,8 +721,7 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
 
         NotificationGuts guts = spy(new NotificationGuts(mContext, null));
         when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -711,8 +749,8 @@
                 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */,
                 null /* onSettingsClick */, null /* onAppSettingsClick */ ,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true, true /* isUserSentimentNegative */, false /* isNoisy */,
-                IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                true, true /* isUserSentimentNegative */,  /* isNoisy */
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
 
@@ -731,8 +769,7 @@
                 null /* onSettingsClick */, null /* onAppSettingsClick */,
                 true /* provisioned */,
                 false /* isNonblockable */, true /* isForBlockingHelper */,
-                true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                true /* isUserSentimentNegative */, IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         mTestableLooper.processAllMessages();
@@ -755,7 +792,7 @@
                 true /* isForBlockingHelper */,
                 true,
                 false /* isUserSentimentNegative */,
-                false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         NotificationGuts guts = mock(NotificationGuts.class);
         doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
         mNotificationInfo.setGutsParent(guts);
@@ -770,7 +807,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
 
@@ -784,9 +821,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -817,7 +854,7 @@
                 false /* isNonblockable */,
                 true /* isForBlockingHelper */,
                 true /* isUserSentimentNegative */,
-                false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -839,7 +876,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
 
@@ -854,7 +891,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -875,7 +912,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -893,9 +930,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.findViewById(R.id.undo).performClick();
         waitForStopButton();
@@ -914,8 +951,8 @@
     public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -937,9 +974,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_silent).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -958,9 +995,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_alert).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -980,9 +1017,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_silent).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -1002,9 +1039,9 @@
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_LOW);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_alert).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -1019,11 +1056,11 @@
     }
 
     @Test
-    public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
+    public void testCloseControlsDoesNotUpdateMinIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1038,10 +1075,10 @@
     public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(false, false);
 
@@ -1056,9 +1093,9 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                }, null, null, true, true, IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         mTestableLooper.processAllMessages();
         ensureNoUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
@@ -1074,10 +1111,10 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
-                }, null, null, true, false, false, IMPORTANCE_DEFAULT,
-                NotificationInfo.ACTION_NONE);
+                }, null, null, true, false, IMPORTANCE_DEFAULT
+        );
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
@@ -1091,124 +1128,6 @@
     }
 
     @Test
-    public void testDisplaySettingsLink() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
-                (View v, Intent intent) -> {
-                    latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(View.VISIBLE, settingsLink.getVisibility());
-        settingsLink.performClick();
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testDisplaySettingsLink_multipleChannels() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                (View v, Intent intent) -> {
-                    latch.countDown();
-                }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(View.VISIBLE, settingsLink.getVisibility());
-        settingsLink.performClick();
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    public void testNoSettingsLink_noHandlingActivity() throws Exception {
-        final String settingsText = "work chats";
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(null);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
-                null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-    @Test
-    public void testNoSettingsLink_noLinkText() throws Exception {
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-    @Test
-    public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
-        final String settingsText = "work chats";
-        final ResolveInfo ri = new ResolveInfo();
-        ri.activityInfo = new ActivityInfo();
-        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
-        ri.activityInfo.name = "something";
-        List<ResolveInfo> ris = new ArrayList<>();
-        ris.add(ri);
-        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
-                .setSettingsText(settingsText).build();
-        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
-                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
-
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
-                true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
-        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
-        assertEquals(GONE, settingsLink.getVisibility());
-    }
-
-
-    @Test
     public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
         assertFalse(mNotificationInfo.willBeRemoved());
     }
@@ -1219,7 +1138,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                IMPORTANCE_DEFAULT);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -1231,10 +1150,10 @@
     public void testUndoText_block() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
         assertTrue(confirmationText.getText().toString().contains("won't see"));
@@ -1244,10 +1163,10 @@
     public void testUndoText_silence() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_silent).performClick();
         waitForUndoButton();
         TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
         assertEquals(mContext.getString(R.string.notification_channel_silenced),
@@ -1258,10 +1177,10 @@
     public void testUndoText_unsilence() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.toggle_silent).performClick();
+        mNotificationInfo.findViewById(R.id.int_alert).performClick();
         waitForUndoButton();
         TextView confirmationText = mNotificationInfo.findViewById(R.id.confirmation_text);
         assertEquals(mContext.getString(R.string.notification_channel_unsilenced),
@@ -1272,10 +1191,10 @@
     public void testNoHeaderOnConfirmation() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         assertEquals(GONE, mNotificationInfo.findViewById(R.id.header).getVisibility());
     }
@@ -1284,69 +1203,13 @@
     public void testHeaderOnUndo() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
+                IMPORTANCE_DEFAULT);
 
-        mNotificationInfo.findViewById(R.id.block).performClick();
+        mNotificationInfo.findViewById(R.id.int_block).performClick();
         waitForUndoButton();
         mNotificationInfo.findViewById(R.id.undo).performClick();
         waitForStopButton();
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
     }
-
-    @Test
-    public void testBindNotificationWithInitialBlockAction() throws Exception {
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testBindNotificationWithInitialSilenceAction() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance());
-    }
-
-    @Test
-    public void testBindNotificationWithInitialUnSilenceAction() throws Exception {
-        mNotificationChannel.setImportance(IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false,
-                true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT);
-        waitForUndoButton();
-        mNotificationInfo.handleCloseControls(true, false);
-
-        mTestableLooper.processAllMessages();
-        ArgumentCaptor<NotificationChannel> updated =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), eq(TEST_UID), updated.capture());
-        assertTrue((updated.getValue().getUserLockedFields()
-                & USER_LOCKED_IMPORTANCE) != 0);
-        assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance());
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 906e718..51492da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -25,7 +25,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Notification;
+import android.app.NotificationChannel;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -35,6 +35,7 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationData;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -43,19 +44,26 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper()
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class NotificationMenuRowTest extends LeakCheckedTest {
 
+    private ExpandableNotificationRow mRow;
+
     @Before
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+        mRow = mock(ExpandableNotificationRow.class);
+        NotificationData.Entry entry = new NotificationData.Entry(
+                mock(StatusBarNotification.class));
+        entry.channel = mock(NotificationChannel.class);
+        when(mRow.getEntry()).thenReturn(entry);
     }
 
     @Test
     public void testAttachDetach() {
         NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         ViewUtils.attachView(row.getMenuView());
         TestableLooper.get(this).processAllMessages();
         ViewUtils.detachView(row.getMenuView());
@@ -65,9 +73,9 @@
     @Test
     public void testRecreateMenu() {
         NotificationMenuRowPlugin row = new NotificationMenuRow(mContext);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         assertTrue(row.getMenuView() != null);
-        row.createMenu(null, null);
+        row.createMenu(mRow, null);
         assertTrue(row.getMenuView() != null);
     }
 
@@ -81,12 +89,7 @@
     @Test
     public void testNoAppOpsInSlowSwipe() {
         NotificationMenuRow row = new NotificationMenuRow(mContext);
-        Notification n = mock(Notification.class);
-        StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getNotification()).thenReturn(n);
-        ExpandableNotificationRow parent = mock(ExpandableNotificationRow.class);
-        when(parent.getStatusBarNotification()).thenReturn(sbn);
-        row.createMenu(parent, null);
+        row.createMenu(mRow, null);
 
         ViewGroup container = (ViewGroup) row.getMenuView();
         // one for snooze and one for noti blocking
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index a4a111a..662e016 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -26,11 +26,11 @@
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -45,7 +45,7 @@
 public class NotificationRoundnessManagerTest extends SysuiTestCase {
 
     private NotificationRoundnessManager mRoundnessManager = new NotificationRoundnessManager();
-    private HashSet<View> mAnimatedChildren = new HashSet<>();
+    private HashSet<ExpandableView> mAnimatedChildren = new HashSet<>();
     private Runnable mRoundnessCallback = mock(Runnable.class);
     private ExpandableNotificationRow mFirst;
     private ExpandableNotificationRow mSecond;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 74ce5f6..e65e806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -325,7 +325,9 @@
 
         // add notification
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        when(row.isClearable()).thenReturn(true);
+        NotificationData.Entry entry = mock(NotificationData.Entry.class);
+        when(row.getEntry()).thenReturn(entry);
+        when(entry.isClearable()).thenReturn(true);
         mStackScroller.addContainerView(row);
 
         mStackScroller.onUpdateRowStates();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 93d8aad..d99e46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -40,7 +40,7 @@
 import org.mockito.Mockito;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper(setAsMainLooper = true)
+@RunWithLooper()
 @SmallTest
 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 10b0d83..c99e766 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -81,12 +81,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getEntry(), mHeadsUpStatusBarView.getShowingEntry());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
     }
 
@@ -95,12 +95,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertTrue(mHeadsUpAppearanceController.isShown());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertFalse(mHeadsUpAppearanceController.isShown());
     }
 
@@ -109,12 +109,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 0.0f, 0.0f);
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(mFirst.getHeaderVisibleAmount(), 1.0f, 0.0f);
     }
 
@@ -125,12 +125,12 @@
         mFirst.setPinned(true);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         when(mHeadsUpManager.getTopEntry()).thenReturn(mFirst.getEntry());
-        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpPinned(mFirst.getEntry());
         Assert.assertEquals(View.INVISIBLE, mOperatorNameView.getVisibility());
 
         mFirst.setPinned(false);
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
-        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst);
+        mHeadsUpAppearanceController.onHeadsUpUnPinned(mFirst.getEntry());
         Assert.assertEquals(View.VISIBLE, mOperatorNameView.getVisibility());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 1070795..44deb10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -97,7 +97,7 @@
     @Test
     public void testCanRemoveImmediately_notTopEntry() {
         NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
-        laterEntry.row = mRow;
+        laterEntry.setRow(mRow);
         mHeadsUpManager.showNotification(mEntry);
         mHeadsUpManager.showNotification(laterEntry);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index f8ad298..31014b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -37,7 +37,6 @@
     private static final int EMPTY_MARGIN = 0;
     private static final int EMPTY_HEIGHT = 0;
     private static final boolean SECURE_LOCKED = false;
-    private static final boolean PULSING_NO = false;
     private static final float ZERO_DRAG = 0.f;
     private static final float OPAQUE = 1.f;
     private static final float TRANSPARENT = 0.f;
@@ -48,6 +47,7 @@
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
+    private boolean mPulsing;
 
     @Before
     public void setUp() {
@@ -171,6 +171,156 @@
         assertThat(mClockPosition.clockAlpha).isEqualTo(TRANSPARENT);
     }
 
+    @Test
+    public void notifPositionMiddleOfScreenOnAOD() {
+        // GIVEN on AOD and both stack scroll and clock have 0 height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
+        // GIVEN on AOD and clock has a nonzero height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = 100;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockOnAOD() {
+        // GIVEN on AOD and clock has a nonzero height
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is, unfortunately, the entire screen.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(SCREEN_HEIGHT);
+    }
+
+    @Test
+    public void notifPositionWhilePulsingOnAOD() {
+        // GIVEN on AOD and pulsing
+        givenAOD();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPulsing = true;
+        mClockPositionAlgorithm.setPulsingPadding(200);
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding doesn't adjust for pulsing.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionMiddleOfScreenOnLockScreen() {
+        // GIVEN on lock screen and both stack scroll and clock have 0 height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionAdjustsForStackHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for the expanded notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(750);
+    }
+
+    @Test
+    public void notifPositionAdjustsForClockHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = 200;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for both clock and notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
+        // GIVEN on lock screen and stack scroller has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = 500;
+        mKeyguardStatusHeight = 200;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for both clock and notif stack.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(810);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockOnLockScreen() {
+        // GIVEN on lock screen and clock has a nonzero height
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding is half of the screen (SCREEN_HEIGHT / 2).
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1000);
+    }
+
+    @Test
+    public void notifPositionWithFullDragOnLockScreen() {
+        // GIVEN the lock screen is dragged up
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPanelExpansion = 0.f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding is zero.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+    }
+
+    @Test
+    public void notifPositionWithLargeClockFullDragOnLockScreen() {
+        // GIVEN the lock screen is dragged up and a full screen clock
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = SCREEN_HEIGHT;
+        mPanelExpansion = 0.f;
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding is zero.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
+    }
+
+    @Test
+    public void notifPositionWhilePulsingOnLockScreen() {
+        // GIVEN on lock screen and pulsing
+        givenLockScreen();
+        mNotificationStackHeight = EMPTY_HEIGHT;
+        mKeyguardStatusHeight = EMPTY_HEIGHT;
+        mPulsing = true;
+        mClockPositionAlgorithm.setPulsingPadding(200);
+        // WHEN the clock position algorithm is run
+        positionClock();
+        // THEN the notif padding adjusts for pulsing.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(1200);
+    }
+
     private void givenAOD() {
         mPanelExpansion = 1.f;
         mDark = 1.f;
@@ -184,7 +334,7 @@
     private void positionClock() {
         mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
                 mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight, mDark, SECURE_LOCKED,
-                PULSING_NO, ZERO_DRAG);
+                mPulsing, ZERO_DRAG);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 96c57f2..c3bc511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -149,7 +149,8 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -166,12 +167,14 @@
         Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         Entry childEntry = mGroupTestHelper.createChildNotification();
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
 
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
         // Alert is immediately removed from summary, and we show child as its content is inflated.
@@ -185,7 +188,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         NotificationData.Entry childEntry2 =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
         mHeadsUpManager.showNotification(summaryEntry);
@@ -199,10 +203,12 @@
         mGroupManager.onEntryAdded(childEntry2);
 
         // Child entry finishes its inflation.
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(true);
         mGroupAlertTransferHelper.onInflationFinished(childEntry);
 
-        verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+        verify(childEntry.getRow(), times(1)).freeContentViewWhenSafe(mHeadsUpManager
+            .getContentFlag());
         assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
     }
 
@@ -212,7 +218,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -229,7 +236,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -251,7 +259,8 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationData.Entry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+        when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
+            .thenReturn(false);
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
         mGroupManager.onEntryAdded(summaryEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 1483ae5..b0bd1fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -100,7 +100,7 @@
         mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
 
         assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
-        assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -143,9 +143,8 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 
     @Test
@@ -161,8 +160,7 @@
 
         // Child entries that are heads upped should be considered separate groups visually even if
         // they are the same group logically
-        assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
-        assertEquals(summaryEntry.row,
-                mGroupManager.getLogicalGroupSummary(childEntry.notification));
+        assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.notification));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.notification));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 01f44fd4..7ad68eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -81,7 +81,7 @@
                 0 /* postTime */);
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
-        entry.row = row;
+        entry.setRow(row);
         when(row.getEntry()).thenReturn(entry);
         when(row.getStatusBarNotification()).thenReturn(sbn);
         when(row.isInflationFlagSet(anyInt())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 4177cd1..1783d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -38,12 +38,12 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.content.Context;
 import com.android.systemui.R;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.SysuiTestCase;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -78,6 +78,7 @@
         mProxyService = mock(OverviewProxyService.class);
         mProxy = mock(IOverviewProxy.Stub.class);
         doReturn(mProxy).when(mProxyService).getProxy();
+        doReturn(true).when(mProxyService).shouldShowSwipeUpUI();
         mDependency.injectTestDependency(OverviewProxyService.class, mProxyService);
 
         mStatusBar = mock(StatusBar.class);
@@ -106,6 +107,18 @@
     }
 
     @Test
+    public void testNoGesturesWhenSwipeUpDisabled() throws Exception {
+        doReturn(false).when(mProxyService).shouldShowSwipeUpUI();
+        mController.setGestureActions(mockAction(true), null /* swipeDownAction */,
+                null /* swipeLeftAction */, null /* swipeRightAction */);
+
+        MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
+        assertFalse(mController.onInterceptTouchEvent(ev));
+        verify(mNavigationBarView, never()).requestUnbufferedDispatch(ev);
+        assertNull(mController.getCurrentAction());
+    }
+
+    @Test
     public void testHasActionDetectGesturesTouchdown() throws Exception {
         MotionEvent ev = event(MotionEvent.ACTION_DOWN, 1, 1);
 
@@ -395,6 +408,7 @@
         verify(mProxy, times(1)).onQuickScrubStart();
         verify(mProxyService, times(1)).notifyQuickScrubStarted();
         verify(mNavigationBarView, times(1)).updateSlippery();
+        verify(mProxy, never()).onMotionEvent(moveEvent1);
 
         // Move again for scrub
         MotionEvent moveEvent2 = event(MotionEvent.ACTION_MOVE, 200, y);
@@ -402,6 +416,7 @@
         assertEquals(action, mController.getCurrentAction());
         verify(action, times(1)).onGestureMove(200, y);
         verify(mProxy, times(1)).onQuickScrubProgress(1f / 2);
+        verify(mProxy, never()).onMotionEvent(moveEvent2);
 
         // Action up
         MotionEvent upEvent = event(MotionEvent.ACTION_UP, 1, y);
@@ -409,6 +424,7 @@
         assertNull(mController.getCurrentAction());
         verify(action, times(1)).onGestureEnd();
         verify(mProxy, times(1)).onQuickScrubEnd();
+        verify(mProxy, never()).onMotionEvent(upEvent);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 2104721..59a4937 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -16,6 +16,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.StatusBarManager;
@@ -63,9 +64,11 @@
         mContext.putComponent(CommandQueue.class, mCommandQueue);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
+        StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
+        when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
         mStatusBar = new StatusBarNotificationPresenter(mContext,
                 mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
-                mock(StatusBarWindowView.class), mock(NotificationListContainerViewGroup.class),
+                statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
                 mock(ActivityLaunchAnimator.Callback.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index b6e3fc1..506fa97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -62,6 +62,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -430,12 +431,18 @@
 
     private void setSmartActions(String[] actionTitles) {
         mView.resetSmartSuggestions(mContainer);
-        mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
+        mView.addSmartActions(
+                new SmartReplyView.SmartActions(createActions(actionTitles), false),
+                mLogger,
+                mEntry);
     }
 
     private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) {
         setSmartReplies(choices);
-        mView.addSmartActions(new SmartReplyView.SmartActions(createActions(actionTitles), false));
+        mView.addSmartActions(
+                new SmartReplyView.SmartActions(createActions(actionTitles), false),
+                mLogger,
+                mEntry);
     }
 
     private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) {
@@ -553,7 +560,7 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any());
+        verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any());
     }
 
     @Test
@@ -738,7 +745,9 @@
     }
 
     private Button inflateActionButton(Notification.Action action) {
-        return mView.inflateActionButton(getContext(), mView, action);
+        return mView.inflateActionButton(getContext(), mView, 0,
+                new SmartReplyView.SmartActions(Collections.singletonList(action), false),
+                mLogger, mEntry);
     }
 
     @Test
@@ -792,4 +801,55 @@
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
         assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(2));
     }
+
+    @Test
+    public void testMeasure_choicesAndActionsPrioritizeActionsOnlyActions() {
+        String[] choices = new String[] {"Reply"};
+        String[] actions = new String[] {"Looooooong actioooon", "second action", "third action"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new String[0], 2,
+                createActions(new String[] {
+                        "Looooooong \nactioooon", "second \naction", "third \naction"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        assertEqualMeasures(expectedView, mView);
+        // smart replies
+        assertReplyButtonHidden(mView.getChildAt(0));
+        // smart actions
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(1));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
+
+    @Test
+    public void testMeasure_choicesAndActionsPrioritizeActions() {
+        String[] choices = new String[] {"Short", "longer reply"};
+        String[] actions = new String[] {"Looooooong actioooon", "second action"};
+
+        // All actions should be displayed as DOUBLE-line smart action buttons.
+        ViewGroup expectedView = buildExpectedView(new String[] {"Short"}, 2,
+                createActions(new String[] {"Looooooong \nactioooon", "second \naction"}));
+        expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+        setSmartRepliesAndActions(choices, actions);
+        mView.measure(
+                MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+                MeasureSpec.UNSPECIFIED);
+
+        Button firstAction = ((Button) mView.getChildAt(1));
+
+        assertEqualMeasures(expectedView, mView);
+        // smart replies
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+        assertReplyButtonHidden(mView.getChildAt(1));
+        // smart actions
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
+        assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+    }
 }
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/AdaptiveIconChangeOverlay/Android.mk
index e642a68..6e3b8cb 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/AdaptiveIconChangeOverlay/Android.mk
@@ -17,14 +17,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := SquareIcon
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := SquareIconOverlay
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
similarity index 72%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
index a1bd582..33da510 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/AdaptiveIconChangeOverlay/AndroidManifest.xml
@@ -16,12 +16,12 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.slim"
-        android:versionCode="1"
-        android:versionName="1.0">
+    package="com.android.theme.icon.square"
+    android:versionCode="1"
+    android:versionName="1.0">
     <overlay android:targetPackage="android"
-        android:category="com.android.internal.experiment_navbar_slim"
+        android:category="android.theme.customization.adaptive_icon_shape"
         android:priority="1"/>
 
-    <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
-</manifest>
\ No newline at end of file
+    <application android:label="@string/square_icon_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml
new file mode 100644
index 0000000..54623f5
--- /dev/null
+++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
+    <string name="config_icon_mask" translatable="false">"M50,0L100,0 100,100 0,100 0,0z"</string>
+    <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
+    <bool name="config_useRoundIcon">false</bool>
+
+</resources>
+
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
similarity index 84%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
index 5ca9d15..64b7d0d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/AdaptiveIconChangeOverlay/res/values/strings.xml
@@ -17,6 +17,7 @@
  */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+    <!-- Square icon overlay [DO NOT TRANSLATE] -->
+    <string name="square_icon_overlay">Square Icons</string>
+
+</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
index e642a68..ecad420 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/Android.mk
@@ -17,14 +17,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarDefault
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarDefaultOverlay
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
similarity index 92%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
index a1bd582..1639fc5 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.slim"
+        package="com.android.internal.experiment.navbar.default"
         android:versionCode="1"
         android:versionName="1.0">
     <overlay android:targetPackage="android"
-        android:category="com.android.internal.experiment_navbar_slim"
+        android:category="com.android.internal.experiment_navbar_default"
         android:priority="1"/>
 
     <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
similarity index 70%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
index 4c3571a..d8b69cd 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/config.xml
@@ -17,12 +17,8 @@
  */
 -->
 <resources>
-    <!-- Height of the bottom navigation / system bar. -->
-    <dimen name="navigation_bar_height">36dp</dimen>
-    <!-- Width of the navigation bar when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_width">36dp</dimen>
     <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
-    <dimen name="navigation_bar_frame_width">36dp</dimen>
+    <dimen name="navigation_bar_frame_width">48dp</dimen>
     <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_frame_height">36dp</dimen>
+    <dimen name="navigation_bar_frame_height">48dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
index 5ca9d15..c933290 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarDefaultOverlay/res/values/strings.xml
@@ -18,5 +18,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+    <string name="experiment_navigationbar_overlay">Default Navigation Bar Experiment (48dp)</string>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
index b4b2b16..b4cc34f 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/AndroidManifest.xml
@@ -16,7 +16,7 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.floating"
+        package="com.android.internal.experiment.navbar.type.floating"
         android:versionCode="1"
         android:versionName="1.0">
     <overlay android:targetPackage="android"
diff --git a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
index 6a58453..30bca3c 100644
--- a/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarFloatingOverlay/res/values/config.xml
@@ -21,8 +21,4 @@
     <dimen name="navigation_bar_height">0dp</dimen>
     <!-- Width of the navigation bar when it is placed vertically on the screen -->
     <dimen name="navigation_bar_width">0dp</dimen>
-    <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
-    <dimen name="navigation_bar_frame_height">48dp</dimen>
-    <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_frame_width">48dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
index e642a68..58cf134 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/Android.mk
@@ -17,14 +17,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim24
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay24
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
index a1bd582..aee543a 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.slim"
+        package="com.android.internal.experiment.navbar.slim24"
         android:versionCode="1"
         android:versionName="1.0">
     <overlay android:targetPackage="android"
-        android:category="com.android.internal.experiment_navbar_slim"
+        android:category="com.android.internal.experiment_navbar_slim24"
         android:priority="1"/>
 
     <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-af/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-af/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-am/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-am/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ar/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ar/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-az/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-az/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-b+sr+Latn/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-b+sr+Latn/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-be/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-be/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bg/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bg/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bs/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-bs/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ca/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ca/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-cs/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-cs/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-da/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-da/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-de/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-de/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-el/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-el/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rAU/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rAU/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rCA/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rCA/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rGB/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rGB/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rIN/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rIN/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-en-rXC/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-en-rXC/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es-rUS/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es-rUS/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-es/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-es/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-et/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-et/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-eu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-eu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fa/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fa/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr-rCA/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr-rCA/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-fr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-fr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-gl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-hy/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-hy/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-in/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-in/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-is/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-is/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-it/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-it/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-iw/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-iw/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ja/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ja/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ka/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ka/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-kk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-km/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-km/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ko/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ko/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ky/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ky/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lo/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lo/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lt/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lt/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-lv/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-lv/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mn/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mn/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-mr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-mr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ms/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ms/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-my/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-my/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nb/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nb/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-nl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-nl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rBR/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rBR/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt-rPT/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt-rPT/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pt/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-pt/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ro/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ro/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ru/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ru/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sq/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sq/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sv/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sv/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-sw/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-sw/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-th/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-th/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tl/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tl/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-tr/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-tr/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uk/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uk/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ur/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-ur/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-uz/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-uz/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-vi/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-vi/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rCN/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rCN/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rHK/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rHK/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zh-rTW/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zh-rTW/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
similarity index 100%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-zu/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values-zu/strings.xml
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
similarity index 82%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
rename to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
index 4c3571a..58c653d 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
 -->
 <resources>
     <!-- Height of the bottom navigation / system bar. -->
-    <dimen name="navigation_bar_height">36dp</dimen>
+    <dimen name="navigation_bar_height">24dp</dimen>
     <!-- Width of the navigation bar when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_width">36dp</dimen>
+    <dimen name="navigation_bar_width">24dp</dimen>
     <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
-    <dimen name="navigation_bar_frame_width">36dp</dimen>
+    <dimen name="navigation_bar_frame_width">24dp</dimen>
     <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_frame_height">36dp</dimen>
+    <dimen name="navigation_bar_frame_height">24dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
index 5ca9d15..670bc55 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim24Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (24dp)</string>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
similarity index 88%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
index e642a68..7ebbb74 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/Android.mk
@@ -17,14 +17,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim32
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay32
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
similarity index 93%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
rename to packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
index a1bd582..10cf6a1 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.slim"
+        package="com.android.internal.experiment.navbar.slim32"
         android:versionCode="1"
         android:versionName="1.0">
     <overlay android:targetPackage="android"
-        android:category="com.android.internal.experiment_navbar_slim"
+        android:category="com.android.internal.experiment_navbar_slim32"
         android:priority="1"/>
 
     <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
index 4c3571a..00dd8fe 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
 -->
 <resources>
     <!-- Height of the bottom navigation / system bar. -->
-    <dimen name="navigation_bar_height">36dp</dimen>
+    <dimen name="navigation_bar_height">32dp</dimen>
     <!-- Width of the navigation bar when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_width">36dp</dimen>
+    <dimen name="navigation_bar_width">32dp</dimen>
     <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
-    <dimen name="navigation_bar_frame_width">36dp</dimen>
+    <dimen name="navigation_bar_frame_width">32dp</dimen>
     <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_frame_height">36dp</dimen>
+    <dimen name="navigation_bar_frame_height">32dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
similarity index 95%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
index 5ca9d15..b48661c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim32Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (32dp)</string>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
similarity index 88%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
rename to packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
index e642a68..28354e3 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/Android.mk
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/Android.mk
@@ -17,14 +17,14 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := ExperimentNavigationBarSlim
+LOCAL_RRO_THEME := ExperimentNavigationBarSlim40
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay
+LOCAL_PACKAGE_NAME := ExperimentNavigationBarSlimOverlay40
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_RRO_PACKAGE)
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
similarity index 93%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
index a1bd582..ce8133f 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/AndroidManifest.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/AndroidManifest.xml
@@ -16,11 +16,11 @@
  */
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.internal.experiment.navbar.slim"
+        package="com.android.internal.experiment.navbar.slim40"
         android:versionCode="1"
         android:versionName="1.0">
     <overlay android:targetPackage="android"
-        android:category="com.android.internal.experiment_navbar_slim"
+        android:category="com.android.internal.experiment_navbar_slim40"
         android:priority="1"/>
 
     <application android:label="@string/experiment_navigationbar_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
similarity index 82%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
copy to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
index 4c3571a..4e65f33 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/config.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/config.xml
@@ -18,11 +18,11 @@
 -->
 <resources>
     <!-- Height of the bottom navigation / system bar. -->
-    <dimen name="navigation_bar_height">36dp</dimen>
+    <dimen name="navigation_bar_height">40dp</dimen>
     <!-- Width of the navigation bar when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_width">36dp</dimen>
+    <dimen name="navigation_bar_width">40dp</dimen>
     <!-- Height of the bottom navigation / system bar frame; navigation buttons height. -->
-    <dimen name="navigation_bar_frame_width">36dp</dimen>
+    <dimen name="navigation_bar_frame_width">40dp</dimen>
     <!-- Width of the navigation bar frame when it is placed vertically on the screen -->
-    <dimen name="navigation_bar_frame_height">36dp</dimen>
+    <dimen name="navigation_bar_frame_height">40dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
similarity index 95%
rename from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
rename to packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
index 5ca9d15..8fe3a5c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/packages/overlays/ExperimentNavigationBarSlim40Overlay/res/values/strings.xml
@@ -18,5 +18,5 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
+    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment (40dp)</string>
 </resources>
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml
deleted file mode 100644
index 8cce570..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-as/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"লাহী নেভিগে’শ্বন বাৰ সম্পৰীক্ষা"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml
deleted file mode 100644
index c0ab3b1..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-bn/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"স্লিম নেভিগেশন বার সম্পর্কিত পরীক্ষা"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml
deleted file mode 100644
index 96418ae..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-gu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"સ્લિમ નૅવિગેશન બારનો પ્રયોગ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml
deleted file mode 100644
index ccdddea..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-kn/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ಸ್ಲಿಮ್ ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್ ಪ್ರಯೋಗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml
deleted file mode 100644
index b65afe3..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ml/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"സ്ലിം നാവിഗേഷൻ ബാർ പരീക്ഷണം"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml
deleted file mode 100644
index 6022b7f..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ne/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"पातलो नेभिगेसन पट्टीको परीक्षण"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml
deleted file mode 100644
index 1db9783..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-or/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ସ୍ଲିମ୍‍ ନାଭିଗେସନ୍‍ ବାର୍‍‍‍‍‍ର ପ୍ରୟୋଗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml
deleted file mode 100644
index a782f46..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-pa/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"ਸਲਿਮ ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਪੱਟੀ ਪ੍ਰਯੋਗ"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml
deleted file mode 100644
index a1abb64..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-si/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"සිහින් සංචාලන තීරු අත්දැකීම"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml
deleted file mode 100644
index 9e95c38..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-ta/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"மெலிதான வழிசெலுத்துதல் பட்டி சோதனை"</string>
-</resources>
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml b/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml
deleted file mode 100644
index d273ab7..0000000
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values-te/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="experiment_navigationbar_overlay" msgid="6953777362606036161">"సన్నని నావిగేషన్ పట్టీ ప్రయోగం"</string>
-</resources>
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 8d691ff..12e7376 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -273,33 +273,6 @@
 
     private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent,
             int policyFlags) {
-        if (DEBUG_ALL) Slog.i(LOG_TAG, "dispatchTransformedEvent(event = " + event + ")");
-
-        // If the touchscreen event is within the magnified portion of the screen we have
-        // to change its location to be where the user thinks he is poking the
-        // UI which may have been magnified and panned.
-        if (mMagnificationController.isMagnifying()
-                && event.isFromSource(SOURCE_TOUCHSCREEN)
-                && mMagnificationController.magnificationRegionContains(
-                        event.getX(), event.getY())) {
-            final float scale = mMagnificationController.getScale();
-            final float scaledOffsetX = mMagnificationController.getOffsetX();
-            final float scaledOffsetY = mMagnificationController.getOffsetY();
-            final int pointerCount = event.getPointerCount();
-            PointerCoords[] coords = getTempPointerCoordsWithMinSize(pointerCount);
-            PointerProperties[] properties = getTempPointerPropertiesWithMinSize(
-                    pointerCount);
-            for (int i = 0; i < pointerCount; i++) {
-                event.getPointerCoords(i, coords[i]);
-                coords[i].x = (coords[i].x - scaledOffsetX) / scale;
-                coords[i].y = (coords[i].y - scaledOffsetY) / scale;
-                event.getPointerProperties(i, properties[i]);
-            }
-            event = MotionEvent.obtain(event.getDownTime(),
-                    event.getEventTime(), event.getAction(), pointerCount, properties,
-                    coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, event.getSource(),
-                    event.getFlags());
-        }
         if (DEBUG_EVENT_STREAM) {
             storeEventInto(mDebugOutputEventHistory, event);
             try {
diff --git a/services/art-profile b/services/art-profile
index bdd49de..af9d7a9 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -18849,7 +18849,7 @@
 PLcom/android/server/wm/WindowManagerService;->onSystemUiStarted()V
 PLcom/android/server/wm/WindowManagerService;->openSession(Landroid/view/IWindowSessionCallback;Lcom/android/internal/view/IInputMethodClient;Lcom/android/internal/view/IInputContext;)Landroid/view/IWindowSession;
 PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransition(Ljava/lang/String;IILandroid/os/IRemoteCallback;)V
-PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;)V
+PLcom/android/server/wm/WindowManagerService;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V
 PLcom/android/server/wm/WindowManagerService;->performBootTimeout()V
 PLcom/android/server/wm/WindowManagerService;->performEnableScreen()V
 PLcom/android/server/wm/WindowManagerService;->postWindowRemoveCleanupLocked(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c56f31e..0da07ae 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -175,10 +175,6 @@
         }
     };
 
-    // TODO(b/117779333): move to superclass / create super-class for ShellCommand
-    @GuardedBy("mLock")
-    private boolean mAllowInstantService;
-
     /**
      * Supported modes for Augmented Autofill Smart Suggestions.
      */
@@ -271,6 +267,11 @@
         addCompatibilityModeRequestsLocked(service, userId);
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+    }
+
     @Override // from SystemService
     public void onStart() {
         publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
@@ -290,7 +291,7 @@
     // Called by Shell command.
     void destroySessions(@UserIdInt int userId, IResultReceiver receiver) {
         Slog.i(TAG, "destroySessions() for userId " + userId);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             if (userId != UserHandle.USER_ALL) {
@@ -313,7 +314,7 @@
     // Called by Shell command.
     void listSessions(int userId, IResultReceiver receiver) {
         Slog.i(TAG, "listSessions() for userId " + userId);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final Bundle resultData = new Bundle();
         final ArrayList<String> sessions = new ArrayList<>();
@@ -340,7 +341,7 @@
     // Called by Shell command.
     void reset() {
         Slog.i(TAG, "reset()");
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             visitServicesLocked((s) -> s.destroyLocked());
@@ -351,7 +352,7 @@
     // Called by Shell command.
     void setLogLevel(int level) {
         Slog.i(TAG, "setLogLevel(): " + level);
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -388,7 +389,7 @@
 
     // Called by Shell command.
     int getLogLevel() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             if (sVerbose) return AutofillManager.FLAG_ADD_CLIENT_VERBOSE;
@@ -399,7 +400,7 @@
 
     // Called by Shell command.
     int getMaxPartitions() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (mLock) {
             return sPartitionMaxCount;
@@ -408,8 +409,8 @@
 
     // Called by Shell command.
     void setMaxPartitions(int max) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -433,7 +434,7 @@
 
     // Called by Shell command.
     int getMaxVisibleDatasets() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         synchronized (sLock) {
             return sVisibleDatasetsMaxCount;
@@ -442,8 +443,8 @@
 
     // Called by Shell command.
     void setMaxVisibleDatasets(int max) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+        enforceCallingPermissionForManagement();
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -480,7 +481,7 @@
     // Called by Shell command.
     void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
 
         final FieldClassificationStrategy strategy =
                 new FieldClassificationStrategy(getContext(), UserHandle.USER_CURRENT);
@@ -491,33 +492,16 @@
 
     // Called by Shell command.
     Boolean getFullScreenMode() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
         return sFullScreenMode;
     }
 
     // Called by Shell command.
     void setFullScreenMode(@Nullable Boolean mode) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        enforceCallingPermissionForManagement();
         sFullScreenMode = mode;
     }
 
-    // Called by Shell command.
-    boolean getAllowInstantService() {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        synchronized (mLock) {
-            return mAllowInstantService;
-        }
-    }
-
-    // Called by Shell command.
-    void setAllowInstantService(boolean mode) {
-        getContext().enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
-        Slog.i(TAG, "setAllowInstantService(): " + mode);
-        synchronized (mLock) {
-            mAllowInstantService = mode;
-        }
-    }
-
     private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
@@ -1218,7 +1202,6 @@
                     mAutofillCompatState.dump(prefix, pw);
                     pw.print("from settings: ");
                     pw.println(getWhitelistedCompatModePackagesFromSettings());
-                    pw.print("Allow instant service: "); pw.println(mAllowInstantService);
                     if (mSupportedSmartSuggestionModes != 0) {
                         pw.print("Smart Suggestion modes: ");
                         pw.println(smartSuggestionFlagsToString(mSupportedSmartSuggestionModes));
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 9aa9d7c..af65759 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -40,9 +40,9 @@
 import android.text.format.DateUtils;
 import android.util.Slog;
 
-import com.android.server.AbstractRemoteService;
+import com.android.server.AbstractSinglePendingRequestRemoteService;
 
-final class RemoteFillService extends AbstractRemoteService {
+final class RemoteFillService extends AbstractSinglePendingRequestRemoteService<RemoteFillService> {
 
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
@@ -69,8 +69,8 @@
         mCallbacks = callbacks;
     }
 
-    @Override
-    protected void onConnectedStateChanged(boolean state) {
+    @Override // from AbstractRemoteService
+    protected void handleOnConnectedStateChanged(boolean state) {
         if (mAutoFillService == null) {
             Slog.w(mTag, "onConnectedStateChanged(): null service");
             return;
@@ -82,18 +82,18 @@
         }
     }
 
-    @Override
+    @Override // from AbstractRemoteService
     protected IInterface getServiceInterface(IBinder service) {
         mAutoFillService = IAutoFillService.Stub.asInterface(service);
         return mAutoFillService;
     }
 
-    @Override
+    @Override // from AbstractRemoteService
     protected long getTimeoutIdleBindMillis() {
         return TIMEOUT_IDLE_BIND_MILLIS;
     }
 
-    @Override
+    @Override // from AbstractRemoteService
     protected long getRemoteRequestMillis() {
         return TIMEOUT_REMOTE_REQUEST_MILLIS;
     }
@@ -136,6 +136,19 @@
         scheduleRequest(new PendingSaveRequest(request, this));
     }
 
+    private boolean handleResponseCallbackCommon(
+            @NonNull PendingRequest<RemoteFillService> pendingRequest) {
+        if (isDestroyed()) return false;
+
+        if (mPendingRequest == pendingRequest) {
+            mPendingRequest = null;
+        }
+        if (mPendingRequest == null) {
+            scheduleUnbind();
+        }
+        return true;
+    }
+
     private void dispatchOnFillRequestSuccess(@NonNull PendingFillRequest pendingRequest,
             @Nullable FillResponse response, int requestFlags) {
         mHandler.post(() -> {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8676f7f..4c64507 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -902,7 +902,7 @@
 
     // VultureCallback
     @Override
-    public void onServiceDied(AbstractRemoteService service) {
+    public void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service) {
         Slog.w(TAG, "removing session because service died");
         forceRemoveSelfLocked();
     }
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index 645723e..d1dbbff 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,5 @@
 anniemeng@google.com
-artikz@google.com
 brufino@google.com
 bryanmawhinney@google.com
 ctate@google.com
 jorlow@google.com
-mkarpinski@google.com
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index df46d260b..2bca34d 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -16,12 +16,15 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.content.ContentResolver;
 import android.os.Handler;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -137,7 +140,7 @@
 
     public long getKvBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
             }
             return mKvBackupAgentTimeoutMillis;
@@ -146,7 +149,7 @@
 
     public long getFullBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
             }
             return mFullBackupAgentTimeoutMillis;
@@ -155,7 +158,7 @@
 
     public long getSharedBackupAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
@@ -166,7 +169,7 @@
 
     public long getRestoreAgentTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
             }
             return mRestoreAgentTimeoutMillis;
@@ -175,7 +178,7 @@
 
     public long getRestoreAgentFinishedTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getRestoreAgentFinishedTimeoutMillis(): "
@@ -187,7 +190,7 @@
 
     public long getQuotaExceededTimeoutMillis() {
         synchronized (mLock) {
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(
                         TAG,
                         "getQuotaExceededTimeoutMillis(): "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerConstants.java b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
index ec21961..785d3ca 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerConstants.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.os.Handler;
@@ -24,6 +26,7 @@
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
 import android.util.Slog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
@@ -151,7 +154,7 @@
     // group the calls of these methods in a block syncrhonized on
     // a reference of this object.
     public synchronized long getKeyValueBackupIntervalMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupIntervalMilliseconds(...) returns "
@@ -161,7 +164,7 @@
     }
 
     public synchronized long getKeyValueBackupFuzzMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupFuzzMilliseconds(...) returns "
@@ -171,7 +174,7 @@
     }
 
     public synchronized boolean getKeyValueBackupRequireCharging() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupRequireCharging(...) returns "
@@ -181,7 +184,7 @@
     }
 
     public synchronized int getKeyValueBackupRequiredNetworkType() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getKeyValueBackupRequiredNetworkType(...) returns "
@@ -191,7 +194,7 @@
     }
 
     public synchronized long getFullBackupIntervalMilliseconds() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getFullBackupIntervalMilliseconds(...) returns "
@@ -201,14 +204,14 @@
     }
 
     public synchronized boolean getFullBackupRequireCharging() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
         }
         return mFullBackupRequireCharging;
     }
 
     public synchronized int getFullBackupRequiredNetworkType() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getFullBackupRequiredNetworkType(...) returns "
@@ -219,7 +222,7 @@
 
     /** Returns an array of package names that should be notified whenever a backup finishes. */
     public synchronized String[] getBackupFinishedNotificationReceivers() {
-        if (BackupManagerService.DEBUG_SCHEDULING) {
+        if (DEBUG_SCHEDULING) {
             Slog.v(
                     TAG,
                     "getBackupFinishedNotificationReceivers(...) returns "
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index e4b4bc5..0b06f28 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -16,239 +16,62 @@
 
 package com.android.server.backup;
 
-import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
-
-import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
-import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
-
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.AppGlobals;
-import android.app.IActivityManager;
-import android.app.IBackupAgent;
-import android.app.PendingIntent;
-import android.app.backup.BackupAgent;
-import android.app.backup.BackupManager;
-import android.app.backup.BackupManagerMonitor;
-import android.app.backup.FullBackup;
-import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.IRestoreSession;
 import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.PowerManager.ServiceType;
-import android.os.PowerSaveState;
-import android.os.Process;
 import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.os.WorkSource;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.EventLog;
-import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppWidgetBackupBridge;
-import com.android.server.EventLogTags;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
-import com.android.server.backup.fullbackup.FullBackupEntry;
-import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
-import com.android.server.backup.internal.BackupHandler;
-import com.android.server.backup.internal.ClearDataObserver;
-import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
-import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.ProvisionedObserver;
-import com.android.server.backup.internal.RunBackupReceiver;
-import com.android.server.backup.internal.RunInitializeReceiver;
-import com.android.server.backup.keyvalue.BackupRequest;
-import com.android.server.backup.params.AdbBackupParams;
-import com.android.server.backup.params.AdbParams;
-import com.android.server.backup.params.AdbRestoreParams;
-import com.android.server.backup.params.BackupParams;
-import com.android.server.backup.params.ClearParams;
-import com.android.server.backup.params.ClearRetryParams;
-import com.android.server.backup.params.RestoreParams;
-import com.android.server.backup.restore.ActiveRestoreSession;
-import com.android.server.backup.restore.PerformUnifiedRestoreTask;
-import com.android.server.backup.transport.TransportClient;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.backup.utils.AppBackupUtils;
-import com.android.server.backup.utils.BackupManagerMonitorUtils;
-import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.SparseArrayUtils;
 
-import com.google.android.collect.Sets;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.io.RandomAccessFile;
-import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.Random;
 import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
 
-/** System service that performs backup/restore operations. */
+/**
+ * Definition of the system service that performs backup/restore operations.
+ *
+ * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
+ * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
+ * corresponding backup/restore operation.
+ */
 public class BackupManagerService {
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
     public static final boolean MORE_DEBUG = false;
     public static final boolean DEBUG_SCHEDULING = true;
 
-    // File containing backup-enabled state.  Contains a single byte;
-    // nonzero == enabled.  File missing or contains a zero byte == disabled.
+    // File containing backup-enabled state. Contains a single byte to denote enabled status.
+    // Nonzero is enabled; file missing or a zero byte is disabled.
     private static final String BACKUP_ENABLE_FILE = "backup_enabled";
 
-    // Persistently track the need to do a full init.
-    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
-
-    // System-private key used for backing up an app's widget state.  Must
-    // begin with U+FFxx by convention (we reserve all keys starting
-    // with U+FF00 or higher for system use).
-    public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
-
-    // Name and current contents version of the full-backup manifest file
-    //
-    // Manifest version history:
-    //
-    // 1 : initial release
-    public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
-    public static final int BACKUP_MANIFEST_VERSION = 1;
-
-    // External archive format version history:
-    //
-    // 1 : initial release
-    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
-    // 3 : introduced "_meta" metadata file; no other format change per se
-    // 4 : added support for new device-encrypted storage locations
-    // 5 : added support for key-value packages
-    public static final int BACKUP_FILE_VERSION = 5;
-    public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    public static final String BACKUP_METADATA_FILENAME = "_meta";
-    public static final int BACKUP_METADATA_VERSION = 1;
-    public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
-
-    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
-
-    // Round-robin queue for scheduling full backup passes.
-    private static final int SCHEDULE_FILE_VERSION = 1;
-
-    public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
-    public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
-
-    // Pseudoname that we use for the Package Manager metadata "package".
-    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
-
-    // Retry interval for clear/init when the transport is unavailable
-    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
-
-    public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
-    public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
-    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
-    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
-
-    // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
-    // pending operations list.
-    public static final int OP_PENDING = 0;
-    private static final int OP_ACKNOWLEDGED = 1;
-    private static final int OP_TIMEOUT = -1;
-
-    // Waiting for backup agent to respond during backup operation.
-    public static final int OP_TYPE_BACKUP_WAIT = 0;
-
-    // Waiting for backup agent to respond during restore operation.
-    public static final int OP_TYPE_RESTORE_WAIT = 1;
-
-    // An entire backup operation spanning multiple packages.
-    public static final int OP_TYPE_BACKUP = 2;
-
-    // Time delay for initialization operations that can be delayed so as not to consume too much
-    // CPU on bring-up and increase time-to-UI.
-    private static final long INITIALIZATION_DELAY_MILLIS = 3000;
-
-    // Timeout interval for deciding that a bind or clear-data has taken too long
-    private static final long TIMEOUT_INTERVAL = 10 * 1000;
-
-    // User confirmation timeout for a full backup/restore operation.  It's this long in
-    // order to give them time to enter the backup password.
-    private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
-
-    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
-    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
-    private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
-    private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
-
     // The published binder is a singleton Trampoline object that calls through to the proper code.
     // This indirection lets us turn down the heavy implementation object on the fly without
     // disturbing binders that have been cached elsewhere in the system.
@@ -302,483 +125,25 @@
                 transportManager);
     }
 
-    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
-    private final TransportManager mTransportManager;
+    private UserBackupManagerService mUserBackupManagerService;
 
-    private Context mContext;
-    private PackageManager mPackageManager;
-    private IPackageManager mPackageManagerBinder;
-    private IActivityManager mActivityManager;
-    private PowerManager mPowerManager;
-    private AlarmManager mAlarmManager;
-    private IStorageManager mStorageManager;
-    private BackupManagerConstants mConstants;
-    private PowerManager.WakeLock mWakelock;
-    private BackupHandler mBackupHandler;
-
-    private IBackupManager mBackupManagerBinder;
-
-    private boolean mEnabled;   // access to this is synchronized on 'this'
-    private boolean mProvisioned;
-    private boolean mAutoRestore;
-
-    private PendingIntent mRunBackupIntent;
-    private PendingIntent mRunInitIntent;
-
-    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
-
-    // map UIDs to the set of participating packages under that UID
-    private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
-
-    // Backups that we haven't started yet.  Keys are package names.
-    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
-
-    // locking around the pending-backup management
-    private final Object mQueueLock = new Object();
-
-    // The thread performing the sequence of queued backups binds to each app's agent
-    // in succession.  Bind notifications are asynchronously delivered through the
-    // Activity Manager; use this lock object to signal when a requested binding has
-    // completed.
-    private final Object mAgentConnectLock = new Object();
-    private IBackupAgent mConnectedAgent;
-    private volatile boolean mConnecting;
-
-    private volatile boolean mBackupRunning;
-    private volatile long mLastBackupPass;
-
-    // A similar synchronization mechanism around clearing apps' data for restore
-    private final Object mClearDataLock = new Object();
-    private volatile boolean mClearingData;
-
-    // Used by ADB.
-    private final BackupPasswordManager mBackupPasswordManager;
-    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
-    private final SecureRandom mRng = new SecureRandom();
-
-    // Time when we post the transport registration operation
-    private final long mRegisterTransportsRequestedTime;
-
-    @GuardedBy("mQueueLock")
-    private PerformFullTransportBackupTask mRunningFullBackupTask;
-
-    @GuardedBy("mQueueLock")
-    private ArrayList<FullBackupEntry> mFullBackupQueue;
-
-    @GuardedBy("mPendingRestores")
-    private boolean mIsRestoreInProgress;
-
-    @GuardedBy("mPendingRestores")
-    private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
-
-    private ActiveRestoreSession mActiveRestoreSession;
-
-    // Watch the device provisioning operation during setup
-    private ContentObserver mProvisionedObserver;
-
-    /**
-     * mCurrentOperations contains the list of currently active operations.
-     *
-     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
-     * An operation wraps a BackupRestoreTask within it.
-     * It's the responsibility of this task to remove the operation from this array.
-     *
-     * A BackupRestore task gets notified of ack/timeout for the operation via
-     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
-     * on the mCurrentOpLock.
-     * {@link BackupManagerService#waitUntilOperationComplete(int)} is
-     * used in various places to 'wait' for notifyAll and detect change of pending state of an
-     * operation. So typically, an operation will be removed from this array by:
-     * - BackupRestoreTask#handleCancel and
-     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
-     * these places because waitUntilOperationComplete relies on the operation being present to
-     * determine its completion status.
-     *
-     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
-     * cancel backup tasks.
-     */
-    @GuardedBy("mCurrentOpLock")
-    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
-    private final Object mCurrentOpLock = new Object();
-    private final Random mTokenGenerator = new Random();
-    final AtomicInteger mNextToken = new AtomicInteger();
-
-    // Where we keep our journal files and other bookkeeping.
-    private File mBaseStateDir;
-    private File mDataDir;
-    private File mJournalDir;
-    @Nullable
-    private DataChangedJournal mJournal;
-    private File mFullBackupScheduleFile;
-
-    // Keep a log of all the apps we've ever backed up.
-    private ProcessedPackagesJournal mProcessedPackagesJournal;
-
-    private File mTokenFile;
-    private Set<String> mAncestralPackages = null;
-    private long mAncestralToken = 0;
-    private long mCurrentToken = 0;
-
-    @VisibleForTesting
+    /** Instantiate a new instance of {@link BackupManagerService}. */
     public BackupManagerService(
             Context context,
-            Trampoline parent,
+            Trampoline trampoline,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mPackageManagerBinder = AppGlobals.getPackageManager();
-        mActivityManager = ActivityManager.getService();
-
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
-
-        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
-
-        mAgentTimeoutParameters = new
-                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
-        mAgentTimeoutParameters.start();
-
-        // spin up the backup/restore handler thread
-        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
-
-        // Set up our bookkeeping
-        final ContentResolver resolver = context.getContentResolver();
-        mProvisioned = Settings.Global.getInt(resolver,
-                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
-        mAutoRestore = Settings.Secure.getInt(resolver,
-                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
-
-        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
-        resolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
-                false, mProvisionedObserver);
-
-        mBaseStateDir = baseStateDir;
-        mBaseStateDir.mkdirs();
-        if (!SELinux.restorecon(mBaseStateDir)) {
-            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
-        }
-
-        mDataDir = dataDir;
-
-        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
-
-        // Alarm receivers for scheduled backups & initialization operations
-        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(RUN_BACKUP_ACTION);
-        context.registerReceiver(mRunBackupReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
-        filter = new IntentFilter();
-        filter.addAction(RUN_INITIALIZE_ACTION);
-        context.registerReceiver(mRunInitReceiver, filter,
-                android.Manifest.permission.BACKUP, null);
-
-        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
-        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
-
-        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
-        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
-
-        // Set up the backup-request journaling
-        mJournalDir = new File(mBaseStateDir, "pending");
-        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
-        mJournal = null;        // will be created on first use
-
-        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
-        // We are observing changes to the constants throughout the lifecycle of BMS. This is
-        // because we reference the constants in multiple areas of BMS, which otherwise would
-        // require frequent starting and stopping.
-        mConstants.start();
-
-        // Set up the various sorts of package tracking we do
-        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
-        initPackageTracking();
-
-        // Build our mapping of uid to backup client services.  This implicitly
-        // schedules a backup pass on the Package Manager metadata the first
-        // time anything needs to be backed up.
-        synchronized (mBackupParticipants) {
-            addPackageParticipantsLocked(null);
-        }
-
-        mTransportManager = transportManager;
-        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
-        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
-        mBackupHandler.postDelayed(
-                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
-
-        // Now that we know about valid backup participants, parse any leftover journal files into
-        // the pending backup set
-        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
-
-        // Power management
-        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+        mUserBackupManagerService =
+                new UserBackupManagerService(
+                        context, trampoline, backupThread, baseStateDir, dataDir, transportManager);
     }
 
-
-    public BackupManagerConstants getConstants() {
-        return mConstants;
-    }
-
-    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
-        return mAgentTimeoutParameters;
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
-    public void setContext(Context context) {
-        mContext = context;
-    }
-
-    public PackageManager getPackageManager() {
-        return mPackageManager;
-    }
-
-    public void setPackageManager(PackageManager packageManager) {
-        mPackageManager = packageManager;
-    }
-
-    public IPackageManager getPackageManagerBinder() {
-        return mPackageManagerBinder;
-    }
-
-    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
-        mPackageManagerBinder = packageManagerBinder;
-    }
-
-    public IActivityManager getActivityManager() {
-        return mActivityManager;
-    }
-
-    public void setActivityManager(IActivityManager activityManager) {
-        mActivityManager = activityManager;
-    }
-
-    public AlarmManager getAlarmManager() {
-        return mAlarmManager;
-    }
-
-    public void setAlarmManager(AlarmManager alarmManager) {
-        mAlarmManager = alarmManager;
-    }
-
+    // TODO(b/118520567): Remove when tests are modified to use per-user instance.
     @VisibleForTesting
-    void setPowerManager(PowerManager powerManager) {
-        mPowerManager = powerManager;
-    }
-
-    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
-        mBackupManagerBinder = backupManagerBinder;
-    }
-
-    public TransportManager getTransportManager() {
-        return mTransportManager;
-    }
-
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    public void setEnabled(boolean enabled) {
-        mEnabled = enabled;
-    }
-
-    public boolean isProvisioned() {
-        return mProvisioned;
-    }
-
-    public void setProvisioned(boolean provisioned) {
-        mProvisioned = provisioned;
-    }
-
-    public PowerManager.WakeLock getWakelock() {
-        return mWakelock;
-    }
-
-    /**
-     * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
-     * #getWakelock()}.
-     */
-    @VisibleForTesting
-    public void setWorkSource(@Nullable WorkSource workSource) {
-        // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
-        mWakelock.setWorkSource(workSource);
-    }
-
-    public void setWakelock(PowerManager.WakeLock wakelock) {
-        mWakelock = wakelock;
-    }
-
-    public Handler getBackupHandler() {
-        return mBackupHandler;
-    }
-
-    public void setBackupHandler(BackupHandler backupHandler) {
-        mBackupHandler = backupHandler;
-    }
-
-    public PendingIntent getRunInitIntent() {
-        return mRunInitIntent;
-    }
-
-    public void setRunInitIntent(PendingIntent runInitIntent) {
-        mRunInitIntent = runInitIntent;
-    }
-
-    public HashMap<String, BackupRequest> getPendingBackups() {
-        return mPendingBackups;
-    }
-
-    public void setPendingBackups(
-            HashMap<String, BackupRequest> pendingBackups) {
-        mPendingBackups = pendingBackups;
-    }
-
-    public Object getQueueLock() {
-        return mQueueLock;
-    }
-
-    public boolean isBackupRunning() {
-        return mBackupRunning;
-    }
-
-    public void setBackupRunning(boolean backupRunning) {
-        mBackupRunning = backupRunning;
-    }
-
-    public long getLastBackupPass() {
-        return mLastBackupPass;
-    }
-
-    public void setLastBackupPass(long lastBackupPass) {
-        mLastBackupPass = lastBackupPass;
-    }
-
-    public Object getClearDataLock() {
-        return mClearDataLock;
-    }
-
-    public boolean isClearingData() {
-        return mClearingData;
-    }
-
-    public void setClearingData(boolean clearingData) {
-        mClearingData = clearingData;
-    }
-
-    public boolean isRestoreInProgress() {
-        return mIsRestoreInProgress;
-    }
-
-    public void setRestoreInProgress(boolean restoreInProgress) {
-        mIsRestoreInProgress = restoreInProgress;
-    }
-
-    public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
-        return mPendingRestores;
-    }
-
-    public ActiveRestoreSession getActiveRestoreSession() {
-        return mActiveRestoreSession;
-    }
-
-    public void setActiveRestoreSession(
-            ActiveRestoreSession activeRestoreSession) {
-        mActiveRestoreSession = activeRestoreSession;
-    }
-
-    public SparseArray<Operation> getCurrentOperations() {
-        return mCurrentOperations;
-    }
-
-    public Object getCurrentOpLock() {
-        return mCurrentOpLock;
-    }
-
-    public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
-        return mAdbBackupRestoreConfirmations;
-    }
-
-    public File getBaseStateDir() {
-        return mBaseStateDir;
-    }
-
-    public void setBaseStateDir(File baseStateDir) {
-        mBaseStateDir = baseStateDir;
-    }
-
-    public File getDataDir() {
-        return mDataDir;
-    }
-
-    public void setDataDir(File dataDir) {
-        mDataDir = dataDir;
-    }
-
-    @Nullable
-    public DataChangedJournal getJournal() {
-        return mJournal;
-    }
-
-    public void setJournal(@Nullable DataChangedJournal journal) {
-        mJournal = journal;
-    }
-
-    public SecureRandom getRng() {
-        return mRng;
-    }
-
-    public Set<String> getAncestralPackages() {
-        return mAncestralPackages;
-    }
-
-    public void setAncestralPackages(Set<String> ancestralPackages) {
-        mAncestralPackages = ancestralPackages;
-    }
-
-    public long getAncestralToken() {
-        return mAncestralToken;
-    }
-
-    public void setAncestralToken(long ancestralToken) {
-        mAncestralToken = ancestralToken;
-    }
-
-    public long getCurrentToken() {
-        return mCurrentToken;
-    }
-
-    public void setCurrentToken(long currentToken) {
-        mCurrentToken = currentToken;
-    }
-
-    public ArraySet<String> getPendingInits() {
-        return mPendingInits;
-    }
-
-    /** Clear all pending transport initializations. */
-    public void clearPendingInits() {
-        mPendingInits.clear();
-    }
-
-    public PerformFullTransportBackupTask getRunningFullBackupTask() {
-        return mRunningFullBackupTask;
-    }
-
-    public void setRunningFullBackupTask(
-            PerformFullTransportBackupTask runningFullBackupTask) {
-        mRunningFullBackupTask = runningFullBackupTask;
+    void setUserBackupManagerService(UserBackupManagerService userBackupManagerService) {
+        mUserBackupManagerService = userBackupManagerService;
     }
 
     /**
@@ -819,2092 +184,93 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    /**
-     *  Utility: build a new random integer token. The low bits are the ordinal of the operation for
-     *  near-time uniqueness, and the upper bits are random for app-side unpredictability.
+    /*
+     * The following methods are implementations of IBackupManager methods called from Trampoline.
+     * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
+     * action on the passed in user. Currently this is a straight redirection (see TODO).
      */
-    public int generateRandomIntegerToken() {
-        int token = mTokenGenerator.nextInt();
-        if (token < 0) token = -token;
-        token &= ~0xFF;
-        token |= (mNextToken.incrementAndGet() & 0xFF);
-        return token;
+    // TODO (b/118520567): Take in user id and call per-user instance of UserBackupManagerService.
+
+    // ---------------------------------------------
+    // BACKUP AGENT OPERATIONS
+    // ---------------------------------------------
+
+    /**
+     * An app's backup agent calls this method to let the service know that there's new data to
+     * backup for their app {@code packageName}. Only used for apps participating in key-value
+     * backup.
+     */
+    public void dataChanged(String packageName) {
+        mUserBackupManagerService.dataChanged(packageName);
     }
 
     /**
-     * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
-     * non-lifecycle agent instance, so we manually set up the context topology for it.
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
      */
-    public BackupAgent makeMetadataAgent() {
-        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
-        pmAgent.attach(mContext);
-        pmAgent.onCreate();
-        return pmAgent;
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        mUserBackupManagerService.agentConnected(packageName, agentBinder);
     }
 
     /**
-     * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+     * called from the {@link ActivityManager}.
      */
-    public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
-        PackageManagerBackupAgent pmAgent =
-                new PackageManagerBackupAgent(mPackageManager, packages);
-        pmAgent.attach(mContext);
-        pmAgent.onCreate();
-        return pmAgent;
-    }
-
-    private void initPackageTracking() {
-        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
-
-        // Remember our ancestral dataset
-        mTokenFile = new File(mBaseStateDir, "ancestral");
-        try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
-                new FileInputStream(mTokenFile)))) {
-            int version = tokenStream.readInt();
-            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
-                mAncestralToken = tokenStream.readLong();
-                mCurrentToken = tokenStream.readLong();
-
-                int numPackages = tokenStream.readInt();
-                if (numPackages >= 0) {
-                    mAncestralPackages = new HashSet<>();
-                    for (int i = 0; i < numPackages; i++) {
-                        String pkgName = tokenStream.readUTF();
-                        mAncestralPackages.add(pkgName);
-                    }
-                }
-            }
-        } catch (FileNotFoundException fnf) {
-            // Probably innocuous
-            Slog.v(TAG, "No ancestral data");
-        } catch (IOException e) {
-            Slog.w(TAG, "Unable to read token file", e);
-        }
-
-        mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
-        mProcessedPackagesJournal.init();
-
-        synchronized (mQueueLock) {
-            // Resume the full-data backup queue
-            mFullBackupQueue = readFullBackupSchedule();
-        }
-
-        // Register for broadcasts about package install, etc., so we can
-        // update the provider list.
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(mBroadcastReceiver, filter);
-        // Register for events related to sdcard installation.
-        IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-    }
-
-    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
-        boolean changed = false;
-        ArrayList<FullBackupEntry> schedule = null;
-        List<PackageInfo> apps =
-                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
-
-        if (mFullBackupScheduleFile.exists()) {
-            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
-                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
-                 DataInputStream in = new DataInputStream(bufStream)) {
-                int version = in.readInt();
-                if (version != SCHEDULE_FILE_VERSION) {
-                    Slog.e(TAG, "Unknown backup schedule version " + version);
-                    return null;
-                }
-
-                final int numPackages = in.readInt();
-                schedule = new ArrayList<>(numPackages);
-
-                // HashSet instead of ArraySet specifically because we want the eventual
-                // lookups against O(hundreds) of entries to be as fast as possible, and
-                // we discard the set immediately after the scan so the extra memory
-                // overhead is transient.
-                HashSet<String> foundApps = new HashSet<>(numPackages);
-
-                for (int i = 0; i < numPackages; i++) {
-                    String pkgName = in.readUTF();
-                    long lastBackup = in.readLong();
-                    foundApps.add(pkgName); // all apps that we've addressed already
-                    try {
-                        PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
-                        if (AppBackupUtils.appGetsFullBackup(pkg)
-                                && AppBackupUtils.appIsEligibleForBackup(
-                                pkg.applicationInfo, mPackageManager)) {
-                            schedule.add(new FullBackupEntry(pkgName, lastBackup));
-                        } else {
-                            if (DEBUG) {
-                                Slog.i(TAG, "Package " + pkgName
-                                        + " no longer eligible for full backup");
-                            }
-                        }
-                    } catch (NameNotFoundException e) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Package " + pkgName
-                                    + " not installed; dropping from full backup");
-                        }
-                    }
-                }
-
-                // New apps can arrive "out of band" via OTA and similar, so we also need to
-                // scan to make sure that we're tracking all full-backup candidates properly
-                for (PackageInfo app : apps) {
-                    if (AppBackupUtils.appGetsFullBackup(app)
-                            && AppBackupUtils.appIsEligibleForBackup(
-                            app.applicationInfo, mPackageManager)) {
-                        if (!foundApps.contains(app.packageName)) {
-                            if (MORE_DEBUG) {
-                                Slog.i(TAG, "New full backup app " + app.packageName + " found");
-                            }
-                            schedule.add(new FullBackupEntry(app.packageName, 0));
-                            changed = true;
-                        }
-                    }
-                }
-
-                Collections.sort(schedule);
-            } catch (Exception e) {
-                Slog.e(TAG, "Unable to read backup schedule", e);
-                mFullBackupScheduleFile.delete();
-                schedule = null;
-            }
-        }
-
-        if (schedule == null) {
-            // no prior queue record, or unable to read it.  Set up the queue
-            // from scratch.
-            changed = true;
-            schedule = new ArrayList<>(apps.size());
-            for (PackageInfo info : apps) {
-                if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
-                        info.applicationInfo, mPackageManager)) {
-                    schedule.add(new FullBackupEntry(info.packageName, 0));
-                }
-            }
-        }
-
-        if (changed) {
-            writeFullBackupScheduleAsync();
-        }
-        return schedule;
-    }
-
-    private Runnable mFullBackupScheduleWriter = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mQueueLock) {
-                try {
-                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
-                    DataOutputStream bufOut = new DataOutputStream(bufStream);
-                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
-
-                    // version 1:
-                    //
-                    // [int] # of packages in the queue = N
-                    // N * {
-                    //     [utf8] package name
-                    //     [long] last backup time for this package
-                    //     }
-                    int numPackages = mFullBackupQueue.size();
-                    bufOut.writeInt(numPackages);
-
-                    for (int i = 0; i < numPackages; i++) {
-                        FullBackupEntry entry = mFullBackupQueue.get(i);
-                        bufOut.writeUTF(entry.packageName);
-                        bufOut.writeLong(entry.lastBackup);
-                    }
-                    bufOut.flush();
-
-                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
-                    FileOutputStream out = af.startWrite();
-                    out.write(bufStream.toByteArray());
-                    af.finishWrite(out);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Unable to write backup schedule!", e);
-                }
-            }
-        }
-    };
-
-    private void writeFullBackupScheduleAsync() {
-        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
-        mBackupHandler.post(mFullBackupScheduleWriter);
-    }
-
-    private void parseLeftoverJournals() {
-        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
-        for (DataChangedJournal journal : journals) {
-            if (!journal.equals(mJournal)) {
-                try {
-                    journal.forEach(packageName -> {
-                        Slog.i(TAG, "Found stale backup journal, scheduling");
-                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
-                        dataChangedImpl(packageName);
-                    });
-                } catch (IOException e) {
-                    Slog.e(TAG, "Can't read " + journal, e);
-                }
-            }
-        }
-    }
-
-    /** Used for generating random salts or passwords. */
-    public byte[] randomBytes(int bits) {
-        byte[] array = new byte[bits / 8];
-        mRng.nextBytes(array);
-        return array;
-    }
-
-    /** For adb backup/restore. */
-    public boolean setBackupPassword(String currentPw, String newPw) {
-        return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
-    }
-
-    /** For adb backup/restore. */
-    public boolean hasBackupPassword() {
-        return mBackupPasswordManager.hasBackupPassword();
-    }
-
-    /** For adb backup/restore. */
-    public boolean backupPasswordMatches(String currentPw) {
-        return mBackupPasswordManager.backupPasswordMatches(currentPw);
+    public void agentDisconnected(String packageName) {
+        mUserBackupManagerService.agentDisconnected(packageName);
     }
 
     /**
-     * Maintain persistent state around whether need to do an initialize operation. This will lock
-     * on {@link #getQueueLock()}.
+     * Used by a currently-active backup agent to notify the service that it has completed its given
+     * outstanding asynchronous backup/restore operation.
      */
-    public void recordInitPending(
-            boolean isPending, String transportName, String transportDirName) {
-        synchronized (mQueueLock) {
-            if (MORE_DEBUG) {
-                Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
-            }
-
-            File stateDir = new File(mBaseStateDir, transportDirName);
-            File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-
-            if (isPending) {
-                // We need an init before we can proceed with sending backup data.
-                // Record that with an entry in our set of pending inits, as well as
-                // journaling it via creation of a sentinel file.
-                mPendingInits.add(transportName);
-                try {
-                    (new FileOutputStream(initPendingFile)).close();
-                } catch (IOException ioe) {
-                    // Something is badly wrong with our permissions; just try to move on
-                }
-            } else {
-                // No more initialization needed; wipe the journal and reset our state.
-                initPendingFile.delete();
-                mPendingInits.remove(transportName);
-            }
-        }
+    public void opComplete(int token, long result) {
+        mUserBackupManagerService.opComplete(token, result);
     }
 
-    /**
-     * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
-     * expiry), so we must re-upload all saved settings.
-     */
-    public void resetBackupState(File stateFileDir) {
-        synchronized (mQueueLock) {
-            mProcessedPackagesJournal.reset();
+    // ---------------------------------------------
+    // TRANSPORT OPERATIONS
+    // ---------------------------------------------
 
-            mCurrentToken = 0;
-            writeRestoreTokens();
-
-            // Remove all the state files
-            for (File sf : stateFileDir.listFiles()) {
-                // ... but don't touch the needs-init sentinel
-                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
-                    sf.delete();
-                }
-            }
-        }
-
-        // Enqueue a new backup of every participant
-        synchronized (mBackupParticipants) {
-            final int numParticipants = mBackupParticipants.size();
-            for (int i = 0; i < numParticipants; i++) {
-                HashSet<String> participants = mBackupParticipants.valueAt(i);
-                if (participants != null) {
-                    for (String packageName : participants) {
-                        dataChangedImpl(packageName);
-                    }
-                }
-            }
-        }
-    }
-
-    private void onTransportRegistered(String transportName, String transportDirName) {
-        if (DEBUG) {
-            long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
-            Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
-                    + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
-        }
-
-        File stateDir = new File(mBaseStateDir, transportDirName);
-        stateDir.mkdirs();
-
-        File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
-        if (initSentinel.exists()) {
-            synchronized (mQueueLock) {
-                mPendingInits.add(transportName);
-
-                // TODO: pick a better starting time than now + 1 minute
-                long delay = 1000 * 60; // one minute, in milliseconds
-                mAlarmManager.set(AlarmManager.RTC_WAKEUP,
-                        System.currentTimeMillis() + delay, mRunInitIntent);
-            }
-        }
-    }
-
-    // ----- Track installation/removal of packages -----
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
-
-            String action = intent.getAction();
-            boolean replacing = false;
-            boolean added = false;
-            boolean changed = false;
-            Bundle extras = intent.getExtras();
-            String[] pkgList = null;
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
-                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                Uri uri = intent.getData();
-                if (uri == null) {
-                    return;
-                }
-                final String pkgName = uri.getSchemeSpecificPart();
-                if (pkgName != null) {
-                    pkgList = new String[]{pkgName};
-                }
-                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
-
-                // At package-changed we only care about looking at new transport states
-                if (changed) {
-                    final String[] components =
-                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
-
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
-                        for (int i = 0; i < components.length; i++) {
-                            Slog.i(TAG, "   * " + components[i]);
-                        }
-                    }
-
-                    mBackupHandler.post(
-                            () -> mTransportManager.onPackageChanged(pkgName, components));
-                    return; // nothing more to do in the PACKAGE_CHANGED case
-                }
-
-                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
-                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
-                added = true;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
-                added = false;
-                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            }
-
-            if (pkgList == null || pkgList.length == 0) {
-                return;
-            }
-
-            final int uid = extras.getInt(Intent.EXTRA_UID);
-            if (added) {
-                synchronized (mBackupParticipants) {
-                    if (replacing) {
-                        // This is the package-replaced case; we just remove the entry
-                        // under the old uid and fall through to re-add.  If an app
-                        // just added key/value backup participation, this picks it up
-                        // as a known participant.
-                        removePackageParticipantsLocked(pkgList, uid);
-                    }
-                    addPackageParticipantsLocked(pkgList);
-                }
-                // If they're full-backup candidates, add them there instead
-                final long now = System.currentTimeMillis();
-                for (final String packageName : pkgList) {
-                    try {
-                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
-                        if (AppBackupUtils.appGetsFullBackup(app)
-                                && AppBackupUtils.appIsEligibleForBackup(
-                                app.applicationInfo, mPackageManager)) {
-                            enqueueFullBackup(packageName, now);
-                            scheduleNextFullBackupJob(0);
-                        } else {
-                            // The app might have just transitioned out of full-data into
-                            // doing key/value backups, or might have just disabled backups
-                            // entirely.  Make sure it is no longer in the full-data queue.
-                            synchronized (mQueueLock) {
-                                dequeueFullBackupLocked(packageName);
-                            }
-                            writeFullBackupScheduleAsync();
-                        }
-
-                        mBackupHandler.post(
-                                () -> mTransportManager.onPackageAdded(packageName));
-
-                    } catch (NameNotFoundException e) {
-                        // doesn't really exist; ignore it
-                        if (DEBUG) {
-                            Slog.w(TAG, "Can't resolve new app " + packageName);
-                        }
-                    }
-                }
-
-                // Whenever a package is added or updated we need to update
-                // the package metadata bookkeeping.
-                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
-            } else {
-                if (replacing) {
-                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
-                } else {
-                    // Outright removal.  In the full-data case, the app will be dropped
-                    // from the queue when its (now obsolete) name comes up again for
-                    // backup.
-                    synchronized (mBackupParticipants) {
-                        removePackageParticipantsLocked(pkgList, uid);
-                    }
-                }
-                for (final String pkgName : pkgList) {
-                    mBackupHandler.post(
-                            () -> mTransportManager.onPackageRemoved(pkgName));
-                }
-            }
-        }
-    };
-
-    // Add the backup agents in the given packages to our set of known backup participants.
-    // If 'packageNames' is null, adds all backup agents in the whole system.
-    private void addPackageParticipantsLocked(String[] packageNames) {
-        // Look for apps that define the android:backupAgent attribute
-        List<PackageInfo> targetApps = allAgentPackages();
-        if (packageNames != null) {
-            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
-            for (String packageName : packageNames) {
-                addPackageParticipantsLockedInner(packageName, targetApps);
-            }
-        } else {
-            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
-            addPackageParticipantsLockedInner(null, targetApps);
-        }
-    }
-
-    private void addPackageParticipantsLockedInner(String packageName,
-            List<PackageInfo> targetPkgs) {
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "Examining " + packageName + " for backup agent");
-        }
-
-        for (PackageInfo pkg : targetPkgs) {
-            if (packageName == null || pkg.packageName.equals(packageName)) {
-                int uid = pkg.applicationInfo.uid;
-                HashSet<String> set = mBackupParticipants.get(uid);
-                if (set == null) {
-                    set = new HashSet<>();
-                    mBackupParticipants.put(uid, set);
-                }
-                set.add(pkg.packageName);
-                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
-
-                // Schedule a backup for it on general principles
-                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
-                Message msg = mBackupHandler
-                        .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
-                mBackupHandler.sendMessage(msg);
-            }
-        }
-    }
-
-    // Remove the given packages' entries from our known active set.
-    private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
-        if (packageNames == null) {
-            Slog.w(TAG, "removePackageParticipants with null list");
-            return;
-        }
-
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
-                    + " #" + packageNames.length);
-        }
-        for (String pkg : packageNames) {
-            // Known previous UID, so we know which package set to check
-            HashSet<String> set = mBackupParticipants.get(oldUid);
-            if (set != null && set.contains(pkg)) {
-                removePackageFromSetLocked(set, pkg);
-                if (set.isEmpty()) {
-                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
-                    mBackupParticipants.remove(oldUid);
-                }
-            }
-        }
-    }
-
-    private void removePackageFromSetLocked(final HashSet<String> set,
-            final String packageName) {
-        if (set.contains(packageName)) {
-            // Found it.  Remove this one package from the bookkeeping, and
-            // if it's the last participating app under this uid we drop the
-            // (now-empty) set as well.
-            // Note that we deliberately leave it 'known' in the "ever backed up"
-            // bookkeeping so that its current-dataset data will be retrieved
-            // if the app is subsequently reinstalled
-            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
-            set.remove(packageName);
-            mPendingBackups.remove(packageName);
-        }
-    }
-
-    // Returns the set of all applications that define an android:backupAgent attribute
-    private List<PackageInfo> allAgentPackages() {
-        // !!! TODO: cache this and regenerate only when necessary
-        int flags = PackageManager.GET_SIGNING_CERTIFICATES;
-        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
-        int numPackages = packages.size();
-        for (int a = numPackages - 1; a >= 0; a--) {
-            PackageInfo pkg = packages.get(a);
-            try {
-                ApplicationInfo app = pkg.applicationInfo;
-                if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
-                        || app.backupAgentName == null
-                        || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
-                    packages.remove(a);
-                } else {
-                    // we will need the shared library path, so look that up and store it here.
-                    // This is used implicitly when we pass the PackageInfo object off to
-                    // the Activity Manager to launch the app for backup/restore purposes.
-                    app = mPackageManager.getApplicationInfo(pkg.packageName,
-                            PackageManager.GET_SHARED_LIBRARY_FILES);
-                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
-                    pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
-                }
-            } catch (NameNotFoundException e) {
-                packages.remove(a);
-            }
-        }
-        return packages;
-    }
-
-    /**
-     * Called from the backup tasks: record that the given app has been successfully backed up at
-     * least once. This includes both key/value and full-data backups through the transport.
-     */
-    public void logBackupComplete(String packageName) {
-        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
-
-        for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
-            final Intent notification = new Intent();
-            notification.setAction(BACKUP_FINISHED_ACTION);
-            notification.setPackage(receiver);
-            notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
-                    | Intent.FLAG_RECEIVER_FOREGROUND);
-            notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
-            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
-        }
-
-        mProcessedPackagesJournal.addPackage(packageName);
-    }
-
-    /**
-     * Persistently record the current and ancestral backup tokens, as well as the set of packages
-     * with data available in the ancestral dataset.
-     */
-    public void writeRestoreTokens() {
-        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
-            // First, the version number of this record, for futureproofing
-            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
-
-            // Write the ancestral and current tokens
-            af.writeLong(mAncestralToken);
-            af.writeLong(mCurrentToken);
-
-            // Now write the set of ancestral packages
-            if (mAncestralPackages == null) {
-                af.writeInt(-1);
-            } else {
-                af.writeInt(mAncestralPackages.size());
-                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
-                for (String pkgName : mAncestralPackages) {
-                    af.writeUTF(pkgName);
-                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
-                }
-            }
-        } catch (IOException e) {
-            Slog.w(TAG, "Unable to write token file:", e);
-        }
-    }
-
-    /** Fires off a backup agent, blocking until it attaches or times out. */
-    @Nullable
-    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
-        IBackupAgent agent = null;
-        synchronized (mAgentConnectLock) {
-            mConnecting = true;
-            mConnectedAgent = null;
-            try {
-                if (mActivityManager.bindBackupAgent(app.packageName, mode,
-                        UserHandle.USER_OWNER)) {
-                    Slog.d(TAG, "awaiting agent for " + app);
-
-                    // success; wait for the agent to arrive
-                    // only wait 10 seconds for the bind to happen
-                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
-                    while (mConnecting && mConnectedAgent == null
-                            && (System.currentTimeMillis() < timeoutMark)) {
-                        try {
-                            mAgentConnectLock.wait(5000);
-                        } catch (InterruptedException e) {
-                            // just bail
-                            Slog.w(TAG, "Interrupted: " + e);
-                            mConnecting = false;
-                            mConnectedAgent = null;
-                        }
-                    }
-
-                    // if we timed out with no connect, abort and move on
-                    if (mConnecting) {
-                        Slog.w(TAG, "Timeout waiting for agent " + app);
-                        mConnectedAgent = null;
-                    }
-                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
-                    agent = mConnectedAgent;
-                }
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
-        }
-        if (agent == null) {
-            try {
-                mActivityManager.clearPendingBackup();
-            } catch (RemoteException e) {
-                // can't happen - ActivityManager is local
-            }
-        }
-        return agent;
-    }
-
-    /** Unbind from a backup agent. */
-    public void unbindAgent(ApplicationInfo app) {
-        try {
-            mActivityManager.unbindBackupAgent(app);
-        } catch (RemoteException e) {
-            // Can't happen - activity manager is local
-        }
-    }
-
-    /**
-     * Clear an application's data, blocking until the operation completes or times out. If {@code
-     * keepSystemState} is {@code true}, we intentionally do not clear system state that would
-     * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
-     * bringing it into the actual expected state related to the already-restored notification state
-     * etc.
-     */
-    public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
-        // Don't wipe packages marked allowClearUserData=false
-        try {
-            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
-            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "allowClearUserData=false so not wiping "
-                            + packageName);
-                }
-                return;
-            }
-        } catch (NameNotFoundException e) {
-            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
-            return;
-        }
-
-        ClearDataObserver observer = new ClearDataObserver(this);
-
-        synchronized (mClearDataLock) {
-            mClearingData = true;
-            try {
-                mActivityManager.clearApplicationUserData(
-                        packageName, keepSystemState, observer, 0);
-            } catch (RemoteException e) {
-                // can't happen because the activity manager is in this process
-            }
-
-            // only wait 10 seconds for the clear data to happen
-            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
-            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
-                try {
-                    mClearDataLock.wait(5000);
-                } catch (InterruptedException e) {
-                    // won't happen, but still.
-                    mClearingData = false;
-                }
-            }
-        }
-    }
-
-    /**
-     * Get the restore-set token for the best-available restore set for this {@code packageName}:
-     * the active set if possible, else the ancestral one. Returns zero if none available.
-     */
-    public long getAvailableRestoreToken(String packageName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getAvailableRestoreToken");
-
-        long token = mAncestralToken;
-        synchronized (mQueueLock) {
-            if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
-                if (MORE_DEBUG) {
-                    Slog.i(TAG, "App in ever-stored, so using current token");
-                }
-                token = mCurrentToken;
-            }
-        }
-        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
-        return token;
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages}.
-     *
-     * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
-     */
-    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
-        return requestBackup(packages, observer, null, flags);
-    }
-
-    /**
-     * Requests a backup for the inputted {@code packages} with a specified {@link
-     * IBackupManagerMonitor}.
-     */
-    public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
-
-        if (packages == null || packages.length < 1) {
-            Slog.e(TAG, "No packages named for backup request");
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
-                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
-            throw new IllegalArgumentException("No packages are provided for backup");
-        }
-
-        if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
-            BackupObserverUtils.sendBackupFinished(observer,
-                    BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-            final int logTag = mProvisioned
-                    ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
-                    : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
-                    BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
-            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
-        }
-
-        final TransportClient transportClient;
-        final String transportDirName;
-        try {
-            transportDirName =
-                    mTransportManager.getTransportDirName(
-                            mTransportManager.getCurrentTransportName());
-            transportClient =
-                    mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
-        } catch (TransportNotRegisteredException e) {
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
-            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
-                    BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
-                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
-            return BackupManager.ERROR_TRANSPORT_ABORTED;
-        }
-
-        OnTaskFinishedListener listener =
-                caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
-
-        ArrayList<String> fullBackupList = new ArrayList<>();
-        ArrayList<String> kvBackupList = new ArrayList<>();
-        for (String packageName : packages) {
-            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
-                kvBackupList.add(packageName);
-                continue;
-            }
-            try {
-                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
-                        PackageManager.GET_SIGNING_CERTIFICATES);
-                if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
-                        mPackageManager)) {
-                    BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
-                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                    continue;
-                }
-                if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
-                    fullBackupList.add(packageInfo.packageName);
-                } else {
-                    kvBackupList.add(packageInfo.packageName);
-                }
-            } catch (NameNotFoundException e) {
-                BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
-                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
-            }
-        }
-        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
-                fullBackupList.size());
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
-                    + fullBackupList.size() + " full backups, " + kvBackupList.size()
-                    + " k/v backups");
-        }
-
-        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
-
-        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
-        msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
-                observer, monitor, listener, true, nonIncrementalBackup);
-        mBackupHandler.sendMessage(msg);
-        return BackupManager.SUCCESS;
-    }
-
-    /** Cancel all running backups. */
-    public void cancelBackups() {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "cancelBackups() called.");
-        }
-        final long oldToken = Binder.clearCallingIdentity();
-        try {
-            List<Integer> operationsToCancel = new ArrayList<>();
-            synchronized (mCurrentOpLock) {
-                for (int i = 0; i < mCurrentOperations.size(); i++) {
-                    Operation op = mCurrentOperations.valueAt(i);
-                    int token = mCurrentOperations.keyAt(i);
-                    if (op.type == OP_TYPE_BACKUP) {
-                        operationsToCancel.add(token);
-                    }
-                }
-            }
-            for (Integer token : operationsToCancel) {
-                handleCancel(token, true /* cancelAll */);
-            }
-            // We don't want the backup jobs to kick in any time soon.
-            // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
-    }
-
-    /** Schedule a timeout message for the operation identified by {@code token}. */
-    public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
-            int operationType) {
-        if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
-            Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
-                    + Integer.toHexString(token) + " of type " + operationType);
-            return;
-        }
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
-                    + " interval=" + interval + " callback=" + callback);
-        }
-
-        synchronized (mCurrentOpLock) {
-            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
-            Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
-                    token, 0, callback);
-            mBackupHandler.sendMessageDelayed(msg, interval);
-        }
-    }
-
-    private int getMessageIdForOperationType(int operationType) {
-        switch (operationType) {
-            case OP_TYPE_BACKUP_WAIT:
-                return MSG_BACKUP_OPERATION_TIMEOUT;
-            case OP_TYPE_RESTORE_WAIT:
-                return MSG_RESTORE_OPERATION_TIMEOUT;
-            default:
-                Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
-                        + operationType);
-                return -1;
-        }
-    }
-
-    /**
-     * Add an operation to the list of currently running operations. Used for cancellation,
-     * completion and timeout callbacks that act on the operation via the {@code token}.
-     */
-    public void putOperation(int token, Operation operation) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
-                    + operation.type);
-        }
-        synchronized (mCurrentOpLock) {
-            mCurrentOperations.put(token, operation);
-        }
-    }
-
-    /**
-     * Remove an operation from the list of currently running operations. An operation is removed
-     * when it is completed, cancelled, or timed out, and thus no longer running.
-     */
-    public void removeOperation(int token) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
-        }
-        synchronized (mCurrentOpLock) {
-            if (mCurrentOperations.get(token) == null) {
-                Slog.w(TAG, "Duplicate remove for operation. token="
-                        + Integer.toHexString(token));
-            }
-            mCurrentOperations.remove(token);
-        }
-    }
-
-    /** Block until we received an operation complete message (from the agent or cancellation). */
-    public boolean waitUntilOperationComplete(int token) {
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Blocking until operation complete for "
-                    + Integer.toHexString(token));
-        }
-        int finalState = OP_PENDING;
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            while (true) {
-                op = mCurrentOperations.get(token);
-                if (op == null) {
-                    // mysterious disappearance: treat as success with no callback
-                    break;
-                } else {
-                    if (op.state == OP_PENDING) {
-                        try {
-                            mCurrentOpLock.wait();
-                        } catch (InterruptedException e) {
-                        }
-                        // When the wait is notified we loop around and recheck the current state
-                    } else {
-                        if (MORE_DEBUG) {
-                            Slog.d(TAG, "Unblocked waiting for operation token="
-                                    + Integer.toHexString(token));
-                        }
-                        // No longer pending; we're done
-                        finalState = op.state;
-                        break;
-                    }
-                }
-            }
-        }
-
-        removeOperation(token);
-        if (op != null) {
-            mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
-        }
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "operation " + Integer.toHexString(token)
-                    + " complete: finalState=" + finalState);
-        }
-        return finalState == OP_ACKNOWLEDGED;
-    }
-
-    /** Cancel the operation associated with {@code token}. */
-    public void handleCancel(int token, boolean cancelAll) {
-        // Notify any synchronous waiters
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            op = mCurrentOperations.get(token);
-            if (MORE_DEBUG) {
-                if (op == null) {
-                    Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
-                            + " but no op found");
-                }
-            }
-            int state = (op != null) ? op.state : OP_TIMEOUT;
-            if (state == OP_ACKNOWLEDGED) {
-                // The operation finished cleanly, so we have nothing more to do.
-                if (DEBUG) {
-                    Slog.w(TAG, "Operation already got an ack."
-                            + "Should have been removed from mCurrentOperations.");
-                }
-                op = null;
-                mCurrentOperations.delete(token);
-            } else if (state == OP_PENDING) {
-                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
-                op.state = OP_TIMEOUT;
-                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
-                // called after we receive cancel here. We need this op's state there.
-
-                // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
-                // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
-                // doesn't require cancellation.
-                if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
-                    mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
-                }
-            }
-            mCurrentOpLock.notifyAll();
-        }
-
-        // If there's a TimeoutHandler for this event, call it
-        if (op != null && op.callback != null) {
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "   Invoking cancel on " + op.callback);
-            }
-            op.callback.handleCancel(cancelAll);
-        }
-    }
-
-    /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
-    public boolean isBackupOperationInProgress() {
-        synchronized (mCurrentOpLock) {
-            for (int i = 0; i < mCurrentOperations.size(); i++) {
-                Operation op = mCurrentOperations.valueAt(i);
-                if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /** Unbind the backup agent and kill the app if it's a non-system app. */
-    public void tearDownAgentAndKill(ApplicationInfo app) {
-        if (app == null) {
-            // Null means the system package, so just quietly move on.  :)
-            return;
-        }
-
-        try {
-            // unbind and tidy up even on timeout or failure, just in case
-            mActivityManager.unbindBackupAgent(app);
-
-            // The agent was running with a stub Application object, so shut it down.
-            // !!! We hardcode the confirmation UI's package name here rather than use a
-            //     manifest flag!  TODO something less direct.
-            if (app.uid >= Process.FIRST_APPLICATION_UID
-                    && !app.packageName.equals("com.android.backupconfirm")) {
-                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
-                mActivityManager.killApplicationProcess(app.processName, app.uid);
-            } else {
-                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
-            }
-        } catch (RemoteException e) {
-            Slog.d(TAG, "Lost app trying to shut down");
-        }
-    }
-
-    /** For adb backup/restore. */
-    public boolean deviceIsEncrypted() {
-        try {
-            return mStorageManager.getEncryptionState()
-                    != StorageManager.ENCRYPTION_STATE_NONE
-                    && mStorageManager.getPasswordType()
-                    != StorageManager.CRYPT_TYPE_DEFAULT;
-        } catch (Exception e) {
-            // If we can't talk to the storagemanager service we have a serious problem; fail
-            // "secure" i.e. assuming that the device is encrypted.
-            Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
-            return true;
-        }
-    }
-
-    // ----- Full-data backup scheduling -----
-
-    /**
-     * Schedule a job to tell us when it's a good time to run a full backup
-     */
-    public void scheduleNextFullBackupJob(long transportMinLatency) {
-        synchronized (mQueueLock) {
-            if (mFullBackupQueue.size() > 0) {
-                // schedule the next job at the point in the future when the least-recently
-                // backed up app comes due for backup again; or immediately if it's already
-                // due.
-                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
-                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
-                final long interval = mConstants.getFullBackupIntervalMilliseconds();
-                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
-                final long latency = Math.max(transportMinLatency, appLatency);
-                Runnable r = new Runnable() {
-                    @Override
-                    public void run() {
-                        FullBackupJob.schedule(mContext, latency, mConstants);
-                    }
-                };
-                mBackupHandler.postDelayed(r, 2500);
-            } else {
-                if (DEBUG_SCHEDULING) {
-                    Slog.i(TAG, "Full backup queue empty; not scheduling");
-                }
-            }
-        }
-    }
-
-    /**
-     * Remove a package from the full-data queue.
-     */
-    @GuardedBy("mQueueLock")
-    private void dequeueFullBackupLocked(String packageName) {
-        final int numPackages = mFullBackupQueue.size();
-        for (int i = numPackages - 1; i >= 0; i--) {
-            final FullBackupEntry e = mFullBackupQueue.get(i);
-            if (packageName.equals(e.packageName)) {
-                mFullBackupQueue.remove(i);
-            }
-        }
-    }
-
-    /**
-     * Enqueue full backup for the given app, with a note about when it last ran.
-     */
-    public void enqueueFullBackup(String packageName, long lastBackedUp) {
-        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
-        synchronized (mQueueLock) {
-            // First, sanity check that we aren't adding a duplicate.  Slow but
-            // straightforward; we'll have at most on the order of a few hundred
-            // items in this list.
-            dequeueFullBackupLocked(packageName);
-
-            // This is also slow but easy for modest numbers of apps: work backwards
-            // from the end of the queue until we find an item whose last backup
-            // time was before this one, then insert this new entry after it.  If we're
-            // adding something new we don't bother scanning, and just prepend.
-            int which = -1;
-            if (lastBackedUp > 0) {
-                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
-                    final FullBackupEntry entry = mFullBackupQueue.get(which);
-                    if (entry.lastBackup <= lastBackedUp) {
-                        mFullBackupQueue.add(which + 1, newEntry);
-                        break;
-                    }
-                }
-            }
-            if (which < 0) {
-                // this one is earlier than any existing one, so prepend
-                mFullBackupQueue.add(0, newEntry);
-            }
-        }
-        writeFullBackupScheduleAsync();
-    }
-
-    private boolean fullBackupAllowable(String transportName) {
-        if (!mTransportManager.isTransportRegistered(transportName)) {
-            Slog.w(TAG, "Transport not registered; full data backup not performed");
-            return false;
-        }
-
-        // Don't proceed unless we have already established package metadata
-        // for the current dataset via a key/value backup pass.
-        try {
-            String transportDirName = mTransportManager.getTransportDirName(transportName);
-            File stateDir = new File(mBaseStateDir, transportDirName);
-            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
-            if (pmState.length() <= 0) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
-                }
-                return false;
-            }
-        } catch (Exception e) {
-            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Conditions are right for a full backup operation, so run one.  The model we use is
-     * to perform one app backup per scheduled job execution, and to reschedule the job
-     * with zero latency as long as conditions remain right and we still have work to do.
-     *
-     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
-     *
-     * @return Whether ongoing work will continue.  The return value here will be passed
-     * along as the return value to the scheduled job's onStartJob() callback.
-     */
-    public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        final long now = System.currentTimeMillis();
-        final long fullBackupInterval;
-        final long keyValueBackupInterval;
-        synchronized (mConstants) {
-            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
-            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
-        }
-        FullBackupEntry entry = null;
-        long latency = fullBackupInterval;
-
-        if (!mEnabled || !mProvisioned) {
-            // Backups are globally disabled, so don't proceed.  We also don't reschedule
-            // the job driving automatic backups; that job will be scheduled again when
-            // the user enables backup.
-            if (MORE_DEBUG) {
-                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
-                        + " p=" + mProvisioned + "; ignoring");
-            }
-            return false;
-        }
-
-        // Don't run the backup if we're in battery saver mode, but reschedule
-        // to try again in the not-so-distant future.
-        final PowerSaveState result =
-                mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
-        if (result.batterySaverEnabled) {
-            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
-            return false;
-        }
-
-        if (DEBUG_SCHEDULING) {
-            Slog.i(TAG, "Beginning scheduled full backup operation");
-        }
-
-        // Great; we're able to run full backup jobs now.  See if we have any work to do.
-        synchronized (mQueueLock) {
-            if (mRunningFullBackupTask != null) {
-                Slog.e(TAG, "Backup triggered but one already/still running!");
-                return false;
-            }
-
-            // At this point we think that we have work to do, but possibly not right now.
-            // Any exit without actually running backups will also require that we
-            // reschedule the job.
-            boolean runBackup = true;
-            boolean headBusy;
-
-            do {
-                // Recheck each time, because culling due to ineligibility may
-                // have emptied the queue.
-                if (mFullBackupQueue.size() == 0) {
-                    // no work to do so just bow out
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup queue empty; doing nothing");
-                    }
-                    runBackup = false;
-                    break;
-                }
-
-                headBusy = false;
-
-                String transportName = mTransportManager.getCurrentTransportName();
-                if (!fullBackupAllowable(transportName)) {
-                    if (MORE_DEBUG) {
-                        Slog.i(TAG, "Preconditions not met; not running full backup");
-                    }
-                    runBackup = false;
-                    // Typically this means we haven't run a key/value backup yet.  Back off
-                    // full-backup operations by the key/value job's run interval so that
-                    // next time we run, we are likely to be able to make progress.
-                    latency = keyValueBackupInterval;
-                }
-
-                if (runBackup) {
-                    entry = mFullBackupQueue.get(0);
-                    long timeSinceRun = now - entry.lastBackup;
-                    runBackup = (timeSinceRun >= fullBackupInterval);
-                    if (!runBackup) {
-                        // It's too early to back up the next thing in the queue, so bow out
-                        if (MORE_DEBUG) {
-                            Slog.i(TAG, "Device ready but too early to back up next app");
-                        }
-                        // Wait until the next app in the queue falls due for a full data backup
-                        latency = fullBackupInterval - timeSinceRun;
-                        break;  // we know we aren't doing work yet, so bail.
-                    }
-
-                    try {
-                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
-                        if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
-                            // The head app isn't supposed to get full-data backups [any more];
-                            // so we cull it and force a loop around to consider the new head
-                            // app.
-                            if (MORE_DEBUG) {
-                                Slog.i(TAG, "Culling package " + entry.packageName
-                                        + " in full-backup queue but not eligible");
-                            }
-                            mFullBackupQueue.remove(0);
-                            headBusy = true; // force the while() condition
-                            continue;
-                        }
-
-                        final int privFlags = appInfo.applicationInfo.privateFlags;
-                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
-                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
-
-                        if (headBusy) {
-                            final long nextEligible = System.currentTimeMillis()
-                                    + BUSY_BACKOFF_MIN_MILLIS
-                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
-                            if (DEBUG_SCHEDULING) {
-                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                                Slog.i(TAG, "Full backup time but " + entry.packageName
-                                        + " is busy; deferring to "
-                                        + sdf.format(new Date(nextEligible)));
-                            }
-                            // This relocates the app's entry from the head of the queue to
-                            // its order-appropriate position further down, so upon looping
-                            // a new candidate will be considered at the head.
-                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
-                        }
-                    } catch (NameNotFoundException nnf) {
-                        // So, we think we want to back this up, but it turns out the package
-                        // in question is no longer installed.  We want to drop it from the
-                        // queue entirely and move on, but if there's nothing else in the queue
-                        // we should bail entirely.  headBusy cannot have been set to true yet.
-                        runBackup = (mFullBackupQueue.size() > 1);
-                    } catch (RemoteException e) {
-                        // Cannot happen; the Activity Manager is in the same process
-                    }
-                }
-            } while (headBusy);
-
-            if (!runBackup) {
-                if (DEBUG_SCHEDULING) {
-                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
-                }
-                final long deferTime = latency;     // pin for the closure
-                mBackupHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        FullBackupJob.schedule(mContext, deferTime, mConstants);
-                    }
-                });
-                return false;
-            }
-
-            // Okay, the top thing is ready for backup now.  Do it.
-            mFullBackupQueue.remove(0);
-            CountDownLatch latch = new CountDownLatch(1);
-            String[] pkg = new String[]{entry.packageName};
-            mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
-                    this,
-                    /* observer */ null,
-                    pkg,
-                    /* updateSchedule */ true,
-                    scheduledJob,
-                    latch,
-                    /* backupObserver */ null,
-                    /* monitor */ null,
-                    /* userInitiated */ false,
-                    "BMS.beginFullBackup()");
-            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
-            mWakelock.acquire();
-            (new Thread(mRunningFullBackupTask)).start();
-        }
-
-        return true;
-    }
-
-    /**
-     * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
-     * task right away.
-     */
-    public void endFullBackup() {
-        // offload the mRunningFullBackupTask.handleCancel() call to another thread,
-        // as we might have to wait for mCancelLock
-        Runnable endFullBackupRunnable = new Runnable() {
-            @Override
-            public void run() {
-                PerformFullTransportBackupTask pftbt = null;
-                synchronized (mQueueLock) {
-                    if (mRunningFullBackupTask != null) {
-                        pftbt = mRunningFullBackupTask;
-                    }
-                }
-                if (pftbt != null) {
-                    if (DEBUG_SCHEDULING) {
-                        Slog.i(TAG, "Telling running backup to stop");
-                    }
-                    pftbt.handleCancel(true);
-                }
-            }
-        };
-        new Thread(endFullBackupRunnable, "end-full-backup").start();
-    }
-
-    /** Used by both incremental and full restore to restore widget data. */
-    public void restoreWidgetData(String packageName, byte[] widgetData) {
-        // Apply the restored widget state and generate the ID update for the app
-        // TODO: http://b/22388012
-        if (MORE_DEBUG) {
-            Slog.i(TAG, "Incorporating restored widget data");
-        }
-        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
-    }
-
-    // *****************************
-    // NEW UNIFIED RESTORE IMPLEMENTATION
-    // *****************************
-
-    /** Schedule a backup pass for {@code packageName}. */
-    public void dataChangedImpl(String packageName) {
-        HashSet<String> targets = dataChangedTargets(packageName);
-        dataChangedImpl(packageName, targets);
-    }
-
-    private void dataChangedImpl(String packageName, HashSet<String> targets) {
-        // Record that we need a backup pass for the caller.  Since multiple callers
-        // may share a uid, we need to note all candidates within that uid and schedule
-        // a backup pass for each of them.
-        if (targets == null) {
-            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
-                    + " uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        synchronized (mQueueLock) {
-            // Note that this client has made data changes that need to be backed up
-            if (targets.contains(packageName)) {
-                // Add the caller to the set of pending backups.  If there is
-                // one already there, then overwrite it, but no harm done.
-                BackupRequest req = new BackupRequest(packageName);
-                if (mPendingBackups.put(packageName, req) == null) {
-                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
-
-                    // Journal this request in case of crash.  The put()
-                    // operation returned null when this package was not already
-                    // in the set; we want to avoid touching the disk redundantly.
-                    writeToJournalLocked(packageName);
-                }
-            }
-        }
-
-        // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext, mConstants);
-    }
-
-    // Note: packageName is currently unused, but may be in the future
-    private HashSet<String> dataChangedTargets(String packageName) {
-        // If the caller does not hold the BACKUP permission, it can only request a
-        // backup of its own data.
-        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
-                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
-            synchronized (mBackupParticipants) {
-                return mBackupParticipants.get(Binder.getCallingUid());
-            }
-        }
-
-        // a caller with full permission can ask to back up any participating app
-        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
-            return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
-        } else {
-            synchronized (mBackupParticipants) {
-                return SparseArrayUtils.union(mBackupParticipants);
-            }
-        }
-    }
-
-    private void writeToJournalLocked(String str) {
-        try {
-            if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
-            mJournal.addPackage(str);
-        } catch (IOException e) {
-            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
-            mJournal = null;
-        }
-    }
-
-    // ----- IBackupManager binder interface -----
-
-    /** Sent from an app's backup agent to let the service know that there's new data to backup. */
-    public void dataChanged(final String packageName) {
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            // TODO: http://b/22388012
-            // App is running under a non-owner user profile.  For now, we do not back
-            // up data from secondary user profiles.
-            // TODO: backups for all user profiles although don't add backup for profiles
-            // without adding admin control in DevicePolicyManager.
-            if (MORE_DEBUG) {
-                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
-                        + callingUserHandle);
-            }
-            return;
-        }
-
-        final HashSet<String> targets = dataChangedTargets(packageName);
-        if (targets == null) {
-            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
-                    + " uid=" + Binder.getCallingUid());
-            return;
-        }
-
-        mBackupHandler.post(new Runnable() {
-            public void run() {
-                dataChangedImpl(packageName, targets);
-            }
-        });
-    }
-
-    /** Run an initialize operation for the given transport. */
+    /** Run an initialize operation for the given transports {@code transportNames}. */
     public void initializeTransports(String[] transportNames, IBackupObserver observer) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "initializeTransport");
-        Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            mWakelock.acquire();
-            OnTaskFinishedListener listener = caller -> mWakelock.release();
-            mBackupHandler.post(
-                    new PerformInitializeTask(this, transportNames, observer, listener));
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        mUserBackupManagerService.initializeTransports(transportNames, observer);
     }
 
-    /** Clear the given package's backup data from the current transport. */
+    /**
+     * Clear the given package {@code packageName}'s backup data from the transport {@code
+     * transportName}.
+     */
     public void clearBackupData(String transportName, String packageName) {
-        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
-        PackageInfo info;
-        try {
-            info = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNING_CERTIFICATES);
-        } catch (NameNotFoundException e) {
-            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
-            return;
-        }
-
-        // If the caller does not hold the BACKUP permission, it can only request a
-        // wipe of its own backed-up data.
-        Set<String> apps;
-        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
-                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
-            apps = mBackupParticipants.get(Binder.getCallingUid());
-        } else {
-            // a caller with full permission can ask to back up any participating app
-            // !!! TODO: allow data-clear of ANY app?
-            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
-            apps = mProcessedPackagesJournal.getPackagesCopy();
-        }
-
-        if (apps.contains(packageName)) {
-            // found it; fire off the clear request
-            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
-            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
-            synchronized (mQueueLock) {
-                TransportClient transportClient =
-                        mTransportManager
-                                .getTransportClient(transportName, "BMS.clearBackupData()");
-                if (transportClient == null) {
-                    // transport is currently unregistered -- make sure to retry
-                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
-                            new ClearRetryParams(transportName, packageName));
-                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
-                    return;
-                }
-                long oldId = Binder.clearCallingIdentity();
-                OnTaskFinishedListener listener =
-                        caller ->
-                                mTransportManager.disposeOfTransportClient(transportClient, caller);
-                mWakelock.acquire();
-                Message msg = mBackupHandler.obtainMessage(
-                        MSG_RUN_CLEAR,
-                        new ClearParams(transportClient, info, listener));
-                mBackupHandler.sendMessage(msg);
-                Binder.restoreCallingIdentity(oldId);
-            }
-        }
+        mUserBackupManagerService.clearBackupData(transportName, packageName);
     }
 
-    /**
-     * Run a backup pass immediately for any applications that have declared that they have pending
-     * updates.
-     */
-    public void backupNow() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            final PowerSaveState result =
-                    mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
-            if (result.batterySaverEnabled) {
-                if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
-            } else {
-                if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
-                synchronized (mQueueLock) {
-                    // Fire the intent that kicks off the whole shebang...
-                    try {
-                        mRunBackupIntent.send();
-                    } catch (PendingIntent.CanceledException e) {
-                        // should never happen
-                        Slog.e(TAG, "run-backup intent cancelled!");
-                    }
-
-                    // ...and cancel any pending scheduled job, because we've just superseded it
-                    KeyValueBackupJob.cancel(mContext);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Returns {@code true} if the system user has gone through SUW. */
-    public boolean deviceIsProvisioned() {
-        final ContentResolver resolver = mContext.getContentResolver();
-        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
-    }
-
-    /**
-     * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
-     * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
-     * return to the caller until the backup has been completed. It requires on-screen confirmation
-     * by the user.
-     */
-    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
-            boolean compress, boolean doKeyValue, String[] pkgList) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Backup supported only for the device owner");
-        }
-
-        // Validate
-        if (!doAllApps) {
-            if (!includeShared) {
-                // If we're backing up shared data (sdcard or equivalent), then we can run
-                // without any supplied app names.  Otherwise, we'd be doing no work, so
-                // report the error.
-                if (pkgList == null || pkgList.length == 0) {
-                    throw new IllegalArgumentException(
-                            "Backup requested but neither shared nor any apps named");
-                }
-            }
-        }
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            // Doesn't make sense to do a full backup prior to setup
-            if (!deviceIsProvisioned()) {
-                Slog.i(TAG, "Backup not supported before setup");
-                return;
-            }
-
-            if (DEBUG) {
-                Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
-                        + " shared=" + includeShared + " all=" + doAllApps + " system="
-                        + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
-            }
-            Slog.i(TAG, "Beginning adb backup...");
-
-            AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
-                    includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
-                    pkgList);
-            final int token = generateRandomIntegerToken();
-            synchronized (mAdbBackupRestoreConfirmations) {
-                mAdbBackupRestoreConfirmations.put(token, params);
-            }
-
-            // start up the confirmation UI
-            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
-            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
-                Slog.e(TAG, "Unable to launch backup confirmation UI");
-                mAdbBackupRestoreConfirmations.delete(token);
-                return;
-            }
-
-            // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                    0);
-
-            // start the confirmation countdown
-            startConfirmationTimeout(token, params);
-
-            // wait for the backup to be performed
-            if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
-            waitForCompletion(params);
-        } finally {
-            try {
-                fd.close();
-            } catch (IOException e) {
-                Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
-            }
-            Binder.restoreCallingIdentity(oldId);
-            Slog.d(TAG, "Adb backup processing complete.");
-        }
-    }
-
-    /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
-    public void fullTransportBackup(String[] pkgNames) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "fullTransportBackup");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Restore supported only for the device owner");
-        }
-
-        String transportName = mTransportManager.getCurrentTransportName();
-        if (!fullBackupAllowable(transportName)) {
-            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
-        } else {
-            if (DEBUG) {
-                Slog.d(TAG, "fullTransportBackup()");
-            }
-
-            final long oldId = Binder.clearCallingIdentity();
-            try {
-                CountDownLatch latch = new CountDownLatch(1);
-                Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
-                        this,
-                        /* observer */ null,
-                        pkgNames,
-                        /* updateSchedule */ false,
-                        /* runningJob */ null,
-                        latch,
-                        /* backupObserver */ null,
-                        /* monitor */ null,
-                        /* userInitiated */ false,
-                        "BMS.fullTransportBackup()");
-                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
-                mWakelock.acquire();
-                (new Thread(task, "full-transport-master")).start();
-                do {
-                    try {
-                        latch.await();
-                        break;
-                    } catch (InterruptedException e) {
-                        // Just go back to waiting for the latch to indicate completion
-                    }
-                } while (true);
-
-                // We just ran a backup on these packages, so kick them to the end of the queue
-                final long now = System.currentTimeMillis();
-                for (String pkg : pkgNames) {
-                    enqueueFullBackup(pkg, now);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(oldId);
-            }
-        }
-
-        if (DEBUG) {
-            Slog.d(TAG, "Done with full transport backup.");
-        }
-    }
-
-    /**
-     * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
-     * confirmation.
-     */
-    public void adbRestore(ParcelFileDescriptor fd) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
-
-        final int callingUserHandle = UserHandle.getCallingUserId();
-        // TODO: http://b/22388012
-        if (callingUserHandle != UserHandle.USER_SYSTEM) {
-            throw new IllegalStateException("Restore supported only for the device owner");
-        }
-
-        long oldId = Binder.clearCallingIdentity();
-
-        try {
-            // Check whether the device has been provisioned -- we don't handle
-            // full restores prior to completing the setup process.
-            if (!deviceIsProvisioned()) {
-                Slog.i(TAG, "Full restore not permitted before setup");
-                return;
-            }
-
-            Slog.i(TAG, "Beginning restore...");
-
-            AdbRestoreParams params = new AdbRestoreParams(fd);
-            final int token = generateRandomIntegerToken();
-            synchronized (mAdbBackupRestoreConfirmations) {
-                mAdbBackupRestoreConfirmations.put(token, params);
-            }
-
-            // start up the confirmation UI
-            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
-            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
-                Slog.e(TAG, "Unable to launch restore confirmation");
-                mAdbBackupRestoreConfirmations.delete(token);
-                return;
-            }
-
-            // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
-                    0);
-
-            // start the confirmation countdown
-            startConfirmationTimeout(token, params);
-
-            // wait for the restore to be performed
-            if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
-            waitForCompletion(params);
-        } finally {
-            try {
-                fd.close();
-            } catch (IOException e) {
-                Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
-            }
-            Binder.restoreCallingIdentity(oldId);
-            Slog.i(TAG, "adb restore processing complete.");
-        }
-    }
-
-    private boolean startConfirmationUi(int token, String action) {
-        try {
-            Intent confIntent = new Intent(action);
-            confIntent.setClassName("com.android.backupconfirm",
-                    "com.android.backupconfirm.BackupRestoreConfirmation");
-            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
-            confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
-        } catch (ActivityNotFoundException e) {
-            return false;
-        }
-        return true;
-    }
-
-    private void startConfirmationTimeout(int token, AdbParams params) {
-        if (MORE_DEBUG) {
-            Slog.d(TAG, "Posting conf timeout msg after "
-                    + TIMEOUT_FULL_CONFIRMATION + " millis");
-        }
-        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
-                token, 0, params);
-        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
-    }
-
-    private void waitForCompletion(AdbParams params) {
-        synchronized (params.latch) {
-            while (!params.latch.get()) {
-                try {
-                    params.latch.wait();
-                } catch (InterruptedException e) { /* never interrupted */ }
-            }
-        }
-    }
-
-    /** Called when adb backup/restore has completed. */
-    public void signalAdbBackupRestoreCompletion(AdbParams params) {
-        synchronized (params.latch) {
-            params.latch.set(true);
-            params.latch.notifyAll();
-        }
-    }
-
-    /**
-     * Confirm that the previously-requested full backup/restore operation can proceed. This is used
-     * to require a user-facing disclosure about the operation.
-     */
-    public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
-            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
-        if (DEBUG) {
-            Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
-                    + " allow=" + allow);
-        }
-
-        // TODO: possibly require not just this signature-only permission, but even
-        // require that the specific designated confirmation-UI app uid is the caller?
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
-                "acknowledgeAdbBackupOrRestore");
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-
-            AdbParams params;
-            synchronized (mAdbBackupRestoreConfirmations) {
-                params = mAdbBackupRestoreConfirmations.get(token);
-                if (params != null) {
-                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
-                    mAdbBackupRestoreConfirmations.delete(token);
-
-                    if (allow) {
-                        final int verb = params instanceof AdbBackupParams
-                                ? MSG_RUN_ADB_BACKUP
-                                : MSG_RUN_ADB_RESTORE;
-
-                        params.observer = observer;
-                        params.curPassword = curPassword;
-
-                        params.encryptPassword = encPpassword;
-
-                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
-                        mWakelock.acquire();
-                        Message msg = mBackupHandler.obtainMessage(verb, params);
-                        mBackupHandler.sendMessage(msg);
-                    } else {
-                        Slog.w(TAG, "User rejected full backup/restore operation");
-                        // indicate completion without having actually transferred any data
-                        signalAdbBackupRestoreCompletion(params);
-                    }
-                } else {
-                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** User-configurable enabling/disabling of backups. */
-    public void setBackupEnabled(boolean enable) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupEnabled");
-
-        Slog.i(TAG, "Backup enabled => " + enable);
-
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            boolean wasEnabled = mEnabled;
-            synchronized (this) {
-                writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
-                mEnabled = enable;
-            }
-
-            synchronized (mQueueLock) {
-                if (enable && !wasEnabled && mProvisioned) {
-                    // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext, mConstants);
-                    scheduleNextFullBackupJob(0);
-                } else if (!enable) {
-                    // No longer enabled, so stop running backups
-                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
-
-                    KeyValueBackupJob.cancel(mContext);
-
-                    // This also constitutes an opt-out, so we wipe any data for
-                    // this device from the backend.  We start that process with
-                    // an alarm in order to guarantee wakelock states.
-                    if (wasEnabled && mProvisioned) {
-                        // NOTE: we currently flush every registered transport, not just
-                        // the currently-active one.
-                        List<String> transportNames = new ArrayList<>();
-                        List<String> transportDirNames = new ArrayList<>();
-                        mTransportManager.forEachRegisteredTransport(
-                                name -> {
-                                    final String dirName;
-                                    try {
-                                        dirName =
-                                                mTransportManager
-                                                        .getTransportDirName(name);
-                                    } catch (TransportNotRegisteredException e) {
-                                        // Should never happen
-                                        Slog.e(TAG, "Unexpected unregistered transport", e);
-                                        return;
-                                    }
-                                    transportNames.add(name);
-                                    transportDirNames.add(dirName);
-                                });
-
-                        // build the set of transports for which we are posting an init
-                        for (int i = 0; i < transportNames.size(); i++) {
-                            recordInitPending(
-                                    true,
-                                    transportNames.get(i),
-                                    transportDirNames.get(i));
-                        }
-                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
-                                mRunInitIntent);
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Enable/disable automatic restore of app data at install time. */
-    public void setAutoRestore(boolean doAutoRestore) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setAutoRestore");
-
-        Slog.i(TAG, "Auto restore => " + doAutoRestore);
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            synchronized (this) {
-                Settings.Secure.putInt(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
-                mAutoRestore = doAutoRestore;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    /** Mark the backup service as having been provisioned. */
-    public void setBackupProvisioned(boolean available) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupProvisioned");
-        /*
-         * This is now a no-op; provisioning is simply the device's own setup state.
-         */
-    }
-
-    /** Report whether the backup mechanism is currently enabled. */
-    public boolean isBackupEnabled() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "isBackupEnabled");
-        return mEnabled;    // no need to synchronize just to read it
-    }
-
-    /** Report the name of the currently active transport. */
+    /** Return the name of the currently active transport. */
     public String getCurrentTransport() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getCurrentTransport");
-        String currentTransport = mTransportManager.getCurrentTransportName();
-        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
-        return currentTransport;
+        return mUserBackupManagerService.getCurrentTransport();
     }
 
     /**
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
-    @Nullable
     public ComponentName getCurrentTransportComponent() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
-        long oldId = Binder.clearCallingIdentity();
-        try {
-            return mTransportManager.getCurrentTransportComponent();
-        } catch (TransportNotRegisteredException e) {
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        return mUserBackupManagerService.getCurrentTransportComponent();
     }
 
     /** Report all known, available backup transports by name. */
     public String[] listAllTransports() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "listAllTransports");
-
-        return mTransportManager.getRegisteredTransportNames();
+        return mUserBackupManagerService.listAllTransports();
     }
 
-    /** Report all known, available backup transports by component. */
+    /** Report all known, available backup transports by {@link ComponentName}. */
     public ComponentName[] listAllTransportComponents() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "listAllTransportComponents");
-        return mTransportManager.getRegisteredTransportComponents();
+        return mUserBackupManagerService.listAllTransportComponents();
     }
 
     /** Report all system whitelisted transports. */
     public String[] getTransportWhitelist() {
-        // No permission check, intentionally.
-        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
-        String[] whitelistedTransports = new String[whitelistedComponents.size()];
-        int i = 0;
-        for (ComponentName component : whitelistedComponents) {
-            whitelistedTransports[i] = component.flattenToShortString();
-            i++;
-        }
-        return whitelistedTransports;
+        return mUserBackupManagerService.getTransportWhitelist();
     }
 
     /**
@@ -2916,18 +282,17 @@
      * @param transportComponent The identity of the transport being described.
      * @param name A {@link String} with the new name for the transport. This is NOT for
      *     identification. MUST NOT be {@code null}.
-     * @param configurationIntent An {@link Intent} that can be passed to
-     *     {@link Context#startActivity} in order to launch the transport's configuration UI. It may
-     *     be {@code null} if the transport does not offer any user-facing configuration UI.
+     * @param configurationIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's configuration UI. It may be
+     *     {@code null} if the transport does not offer any user-facing configuration UI.
      * @param currentDestinationString A {@link String} describing the destination to which the
      *     transport is currently sending data. MUST NOT be {@code null}.
-     * @param dataManagementIntent An {@link Intent} that can be passed to
-     *     {@link Context#startActivity} in order to launch the transport's data-management UI. It
-     *     may be {@code null} if the transport does not offer any user-facing data
-     *     management UI.
+     * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+     *     Context#startActivity} in order to launch the transport's data-management UI. It may be
+     *     {@code null} if the transport does not offer any user-facing data management UI.
      * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
-     *     management affordance. This MUST be {@code null} when dataManagementIntent is
-     *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+     *     management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+     *     null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
      * @throws SecurityException If the UID of the calling process differs from the package UID of
      *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
      */
@@ -2937,9 +302,8 @@
             @Nullable Intent configurationIntent,
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
-            @Nullable String dataManagementLabel) {
-        updateTransportAttributes(
-                Binder.getCallingUid(),
+            String dataManagementLabel) {
+        mUserBackupManagerService.updateTransportAttributes(
                 transportComponent,
                 name,
                 configurationIntent,
@@ -2948,73 +312,16 @@
                 dataManagementLabel);
     }
 
-    @VisibleForTesting
-    void updateTransportAttributes(
-            int callingUid,
-            ComponentName transportComponent,
-            String name,
-            @Nullable Intent configurationIntent,
-            String currentDestinationString,
-            @Nullable Intent dataManagementIntent,
-            @Nullable String dataManagementLabel) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "updateTransportAttributes");
-
-        Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
-        Preconditions.checkNotNull(name, "name can't be null");
-        Preconditions.checkNotNull(
-                currentDestinationString, "currentDestinationString can't be null");
-        Preconditions.checkArgument(
-                (dataManagementIntent == null) == (dataManagementLabel == null),
-                "dataManagementLabel should be null iff dataManagementIntent is null");
-
-        try {
-            int transportUid =
-                    mContext.getPackageManager()
-                            .getPackageUid(transportComponent.getPackageName(), 0);
-            if (callingUid != transportUid) {
-                throw new SecurityException("Only the transport can change its description");
-            }
-        } catch (NameNotFoundException e) {
-            throw new SecurityException("Transport package not found", e);
-        }
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            mTransportManager.updateTransportAttributes(
-                    transportComponent,
-                    name,
-                    configurationIntent,
-                    currentDestinationString,
-                    dataManagementIntent,
-                    dataManagementLabel);
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
     /**
-     * Selects transport {@code transportName} and returns previously selected transport.
+     * Selects transport {@code transportName} and returns the previously selected transport.
      *
      * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} instead.
+     *     ISelectBackupTransportCallback)} instead.
      */
     @Deprecated
     @Nullable
     public String selectBackupTransport(String transportName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "selectBackupTransport");
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            String previousTransportName = mTransportManager.selectTransport(transportName);
-            updateStateForTransport(transportName);
-            Slog.v(TAG, "selectBackupTransport(transport = " + transportName
-                    + "): previous transport = " + previousTransportName);
-            return previousTransportName;
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
+        return mUserBackupManagerService.selectBackupTransport(transportName);
     }
 
     /**
@@ -3023,70 +330,7 @@
      */
     public void selectBackupTransportAsync(
             ComponentName transportComponent, ISelectBackupTransportCallback listener) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
-
-        final long oldId = Binder.clearCallingIdentity();
-        try {
-            String transportString = transportComponent.flattenToShortString();
-            Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
-            mBackupHandler.post(
-                    () -> {
-                        String transportName = null;
-                        int result =
-                                mTransportManager.registerAndSelectTransport(transportComponent);
-                        if (result == BackupManager.SUCCESS) {
-                            try {
-                                transportName =
-                                        mTransportManager.getTransportName(transportComponent);
-                                updateStateForTransport(transportName);
-                            } catch (TransportNotRegisteredException e) {
-                                Slog.e(TAG, "Transport got unregistered");
-                                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
-                            }
-                        }
-
-                        try {
-                            if (transportName != null) {
-                                listener.onSuccess(transportName);
-                            } else {
-                                listener.onFailure(result);
-                            }
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
-                        }
-                    });
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-    }
-
-    private void updateStateForTransport(String newTransportName) {
-        // Publish the name change
-        Settings.Secure.putString(mContext.getContentResolver(),
-                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
-
-        // And update our current-dataset bookkeeping
-        String callerLogString = "BMS.updateStateForTransport()";
-        TransportClient transportClient =
-                mTransportManager.getTransportClient(newTransportName, callerLogString);
-        if (transportClient != null) {
-            try {
-                IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
-                mCurrentToken = transport.getCurrentRestoreSet();
-            } catch (Exception e) {
-                // Oops.  We can't know the current dataset token, so reset and figure it out
-                // when we do the next k/v backup operation on this transport.
-                mCurrentToken = 0;
-                Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
-            }
-            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-        } else {
-            Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
-            // The named transport isn't registered, so we can't know what its current dataset token
-            // is. Reset as above.
-            mCurrentToken = 0;
-        }
+        mUserBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
     }
 
     /**
@@ -3095,18 +339,7 @@
      * returns {@code null}.
      */
     public Intent getConfigurationIntent(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getConfigurationIntent");
-        try {
-            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
-            }
-            return intent;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getConfigurationIntent(transportName);
     }
 
     /**
@@ -3119,36 +352,12 @@
      * @return The current destination string or null if the transport is not registered.
      */
     public String getDestinationString(String transportName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "getDestinationString");
-
-        try {
-            String string = mTransportManager.getTransportCurrentDestinationString(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDestinationString() returning " + string);
-            }
-            return string;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getDestinationString(transportName);
     }
 
     /** Supply the manage-data intent for the given transport. */
     public Intent getDataManagementIntent(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getDataManagementIntent");
-
-        try {
-            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
-            }
-            return intent;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
-            return null;
-        }
+        return mUserBackupManagerService.getDataManagementIntent(transportName);
     }
 
     /**
@@ -3156,443 +365,202 @@
      * transport.
      */
     public String getDataManagementLabel(String transportName) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "getDataManagementLabel");
+        return mUserBackupManagerService.getDataManagementLabel(transportName);
+    }
 
-        try {
-            String label = mTransportManager.getTransportDataManagementLabel(transportName);
-            if (MORE_DEBUG) {
-                Slog.d(TAG, "getDataManagementLabel() returning " + label);
-            }
-            return label;
-        } catch (TransportNotRegisteredException e) {
-            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
-            return null;
-        }
+    // ---------------------------------------------
+    // SETTINGS OPERATIONS
+    // ---------------------------------------------
+
+    /** Enable/disable the backup service. This is user-configurable via backup settings. */
+    public void setBackupEnabled(boolean enable) {
+        mUserBackupManagerService.setBackupEnabled(enable);
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(boolean autoRestore) {
+        mUserBackupManagerService.setAutoRestore(autoRestore);
+    }
+
+    /** Mark the backup service as having been provisioned (device has gone through SUW). */
+    public void setBackupProvisioned(boolean provisioned) {
+        mUserBackupManagerService.setBackupProvisioned(provisioned);
     }
 
     /**
-     * Callback: a requested backup agent has been instantiated. This should only be called from the
-     * {@link ActivityManager}.
+     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
      */
-    public void agentConnected(String packageName, IBinder agentBinder) {
-        synchronized (mAgentConnectLock) {
-            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
-                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
-                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
-                mConnectedAgent = agent;
-                mConnecting = false;
-            } else {
-                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                        + " claiming agent connected");
-            }
-            mAgentConnectLock.notifyAll();
-        }
+    public boolean isBackupEnabled() {
+        return mUserBackupManagerService.isBackupEnabled();
+    }
+
+    // ---------------------------------------------
+    // BACKUP OPERATIONS
+    // ---------------------------------------------
+
+    /** Checks if the given package {@code packageName} is eligible for backup. */
+    public boolean isAppEligibleForBackup(String packageName) {
+        return mUserBackupManagerService.isAppEligibleForBackup(packageName);
     }
 
     /**
-     * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
-     * to come up in the first place, the agentBinder argument will be {@code null}. This should
-     * only be called from the {@link ActivityManager}.
+     * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
      */
-    public void agentDisconnected(String packageName) {
-        // TODO: handle backup being interrupted
-        synchronized (mAgentConnectLock) {
-            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
-                mConnectedAgent = null;
-                mConnecting = false;
-            } else {
-                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                        + " claiming agent disconnected");
-            }
-            mAgentConnectLock.notifyAll();
-        }
+    public String[] filterAppsEligibleForBackup(String[] packages) {
+        return mUserBackupManagerService.filterAppsEligibleForBackup(packages);
     }
 
     /**
-     * An application being installed will need a restore pass, then the {@link PackageManager} will
-     * need to be told when the restore is finished.
+     * Run a backup pass immediately for any key-value backup applications that have declared that
+     * they have pending updates.
+     */
+    public void backupNow() {
+        mUserBackupManagerService.backupNow();
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified callback {@link
+     * IBackupManagerMonitor} for receiving events during the operation.
+     */
+    public int requestBackup(
+            String[] packages, IBackupObserver observer, IBackupManagerMonitor monitor, int flags) {
+        return mUserBackupManagerService.requestBackup(packages, observer, monitor, flags);
+    }
+
+    /** Cancel all running backup operations. */
+    public void cancelBackups() {
+        mUserBackupManagerService.cancelBackups();
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+     * zero latency as long as conditions remain right and we still have work to do.
+     *
+     * @return Whether ongoing work will continue. The return value here will be passed along as the
+     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
+     */
+    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+        return mUserBackupManagerService.beginFullBackup(scheduledJob);
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+     * longer met for running the full backup job.
+     */
+    public void endFullBackup() {
+        mUserBackupManagerService.endFullBackup();
+    }
+
+    /**
+     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+     */
+    public void fullTransportBackup(String[] packageNames) {
+        mUserBackupManagerService.fullTransportBackup(packageNames);
+    }
+
+    // ---------------------------------------------
+    // RESTORE OPERATIONS
+    // ---------------------------------------------
+
+    /**
+     * Used to run a restore pass for an application that is being installed. This should only be
+     * called from the {@link PackageManager}.
      */
     public void restoreAtInstall(String packageName, int token) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
-                    + " attemping install-time restore");
-            return;
-        }
-
-        boolean skip = false;
-
-        long restoreSet = getAvailableRestoreToken(packageName);
-        if (DEBUG) {
-            Slog.v(TAG, "restoreAtInstall pkg=" + packageName
-                    + " token=" + Integer.toHexString(token)
-                    + " restoreSet=" + Long.toHexString(restoreSet));
-        }
-        if (restoreSet == 0) {
-            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
-            skip = true;
-        }
-
-        TransportClient transportClient =
-                mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
-        if (transportClient == null) {
-            if (DEBUG) Slog.w(TAG, "No transport client");
-            skip = true;
-        }
-
-        if (!mAutoRestore) {
-            if (DEBUG) {
-                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
-            }
-            skip = true;
-        }
-
-        if (!skip) {
-            try {
-                // okay, we're going to attempt a restore of this package from this restore set.
-                // The eventual message back into the Package Manager to run the post-install
-                // steps for 'token' will be issued from the restore handling code.
-
-                mWakelock.acquire();
-
-                OnTaskFinishedListener listener = caller -> {
-                    mTransportManager.disposeOfTransportClient(transportClient, caller);
-                    mWakelock.release();
-                };
-
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "Restore at install of " + packageName);
-                }
-                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
-                msg.obj =
-                        RestoreParams.createForRestoreAtInstall(
-                                transportClient,
-                                /* observer */ null,
-                                /* monitor */ null,
-                                restoreSet,
-                                packageName,
-                                token,
-                                listener);
-                mBackupHandler.sendMessage(msg);
-            } catch (Exception e) {
-                // Calling into the transport broke; back off and proceed with the installation.
-                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
-                skip = true;
-            }
-        }
-
-        if (skip) {
-            // Auto-restore disabled or no way to attempt a restore
-
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(
-                        transportClient, "BMS.restoreAtInstall()");
-            }
-
-            // Tell the PackageManager to proceed with the post-install handling for this package.
-            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
-            try {
-                mPackageManagerBinder.finishPackageInstall(token, false);
-            } catch (RemoteException e) { /* can't happen */ }
-        }
-    }
-
-    /** Hand off a restore session. */
-    public IRestoreSession beginRestoreSession(String packageName, String transport) {
-        if (DEBUG) {
-            Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
-                    + " transport=" + transport);
-        }
-
-        boolean needPermission = true;
-        if (transport == null) {
-            transport = mTransportManager.getCurrentTransportName();
-
-            if (packageName != null) {
-                PackageInfo app = null;
-                try {
-                    app = mPackageManager.getPackageInfo(packageName, 0);
-                } catch (NameNotFoundException nnf) {
-                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
-                    throw new IllegalArgumentException("Package " + packageName + " not found");
-                }
-
-                if (app.applicationInfo.uid == Binder.getCallingUid()) {
-                    // So: using the current active transport, and the caller has asked
-                    // that its own package will be restored.  In this narrow use case
-                    // we do not require the caller to hold the permission.
-                    needPermission = false;
-                }
-            }
-        }
-
-        if (needPermission) {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                    "beginRestoreSession");
-        } else {
-            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
-        }
-
-        synchronized (this) {
-            if (mActiveRestoreSession != null) {
-                Slog.i(TAG, "Restore session requested but one already active");
-                return null;
-            }
-            if (mBackupRunning) {
-                Slog.i(TAG, "Restore session requested but currently running backups");
-                return null;
-            }
-            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
-            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
-        }
-        return mActiveRestoreSession;
-    }
-
-    /** Clear the specified restore session. */
-    public void clearRestoreSession(ActiveRestoreSession currentSession) {
-        synchronized (this) {
-            if (currentSession != mActiveRestoreSession) {
-                Slog.e(TAG, "ending non-current restore session");
-            } else {
-                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
-                mActiveRestoreSession = null;
-                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-            }
-        }
+        mUserBackupManagerService.restoreAtInstall(packageName, token);
     }
 
     /**
-     * Note that a currently-active backup agent has notified us that it has completed the given
-     * outstanding asynchronous backup/restore operation.
+     * Begin a restore for the specified package {@code packageName} using the specified transport
+     * {@code transportName}.
      */
-    public void opComplete(int token, long result) {
-        if (MORE_DEBUG) {
-            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
-        }
-        Operation op = null;
-        synchronized (mCurrentOpLock) {
-            op = mCurrentOperations.get(token);
-            if (op != null) {
-                if (op.state == OP_TIMEOUT) {
-                    // The operation already timed out, and this is a late response.  Tidy up
-                    // and ignore it; we've already dealt with the timeout.
-                    op = null;
-                    mCurrentOperations.delete(token);
-                } else if (op.state == OP_ACKNOWLEDGED) {
-                    if (DEBUG) {
-                        Slog.w(TAG, "Received duplicate ack for token="
-                                + Integer.toHexString(token));
-                    }
-                    op = null;
-                    mCurrentOperations.remove(token);
-                } else if (op.state == OP_PENDING) {
-                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
-                    // called after we we receive this call.
-                    op.state = OP_ACKNOWLEDGED;
-                }
-            }
-            mCurrentOpLock.notifyAll();
-        }
-
-        // The completion callback, if any, is invoked on the handler
-        if (op != null && op.callback != null) {
-            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
-            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
-            mBackupHandler.sendMessage(msg);
-        }
+    public IRestoreSession beginRestoreSession(String packageName, String transportName) {
+        return mUserBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
-    /** Checks if the package is eligible for backup. */
-    public boolean isAppEligibleForBackup(String packageName) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
-
-        long oldToken = Binder.clearCallingIdentity();
-        try {
-            String callerLogString = "BMS.isAppEligibleForBackup";
-            TransportClient transportClient =
-                    mTransportManager.getCurrentTransportClient(callerLogString);
-            boolean eligible =
-                    AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
-                            transportClient, packageName, mPackageManager);
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-            }
-            return eligible;
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(String packageName) {
+        return mUserBackupManagerService.getAvailableRestoreToken(packageName);
     }
 
-    /** Returns the inputted packages that are eligible for backup. */
-    public String[] filterAppsEligibleForBackup(String[] packages) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+    // ---------------------------------------------
+    // ADB BACKUP/RESTORE OPERATIONS
+    // ---------------------------------------------
 
-        long oldToken = Binder.clearCallingIdentity();
-        try {
-            String callerLogString = "BMS.filterAppsEligibleForBackup";
-            TransportClient transportClient =
-                    mTransportManager.getCurrentTransportClient(callerLogString);
-            List<String> eligibleApps = new LinkedList<>();
-            for (String packageName : packages) {
-                if (AppBackupUtils
-                        .appIsRunningAndEligibleForBackupWithTransport(
-                                transportClient, packageName, mPackageManager)) {
-                    eligibleApps.add(packageName);
-                }
-            }
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-            }
-            return eligibleApps.toArray(new String[eligibleApps.size()]);
-        } finally {
-            Binder.restoreCallingIdentity(oldToken);
-        }
+    /** Sets the backup password used when running adb backup. */
+    public boolean setBackupPassword(String currentPassword, String newPassword) {
+        return mUserBackupManagerService.setBackupPassword(currentPassword, newPassword);
     }
 
+    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+    public boolean hasBackupPassword() {
+        return mUserBackupManagerService.hasBackupPassword();
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+     * synchronous and does not return to the caller until the backup has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    public void adbBackup(
+            ParcelFileDescriptor fd,
+            boolean includeApks,
+            boolean includeObbs,
+            boolean includeShared,
+            boolean doWidgets,
+            boolean doAllApps,
+            boolean includeSystem,
+            boolean doCompress,
+            boolean doKeyValue,
+            String[] packageNames) {
+        mUserBackupManagerService.adbBackup(
+                fd,
+                includeApks,
+                includeObbs,
+                includeShared,
+                doWidgets,
+                doAllApps,
+                includeSystem,
+                doCompress,
+                doKeyValue,
+                packageNames);
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+     * is synchronous and does not return to the caller until the restore has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    public void adbRestore(ParcelFileDescriptor fd) {
+        mUserBackupManagerService.adbRestore(fd);
+    }
+
+    /**
+     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(
+            int token,
+            boolean allow,
+            String currentPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer) {
+        mUserBackupManagerService.acknowledgeAdbBackupOrRestore(
+                token, allow, currentPassword, encryptionPassword, observer);
+    }
+
+    // ---------------------------------------------
+    //  SERVICE OPERATIONS
+    // ---------------------------------------------
+
     /** Prints service state for 'dumpsys backup'. */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
-
-        long identityToken = Binder.clearCallingIdentity();
-        try {
-            if (args != null) {
-                for (String arg : args) {
-                    if ("-h".equals(arg)) {
-                        pw.println("'dumpsys backup' optional arguments:");
-                        pw.println("  -h       : this help text");
-                        pw.println("  a[gents] : dump information about defined backup agents");
-                        return;
-                    } else if ("agents".startsWith(arg)) {
-                        dumpAgents(pw);
-                        return;
-                    } else if ("transportclients".equals(arg.toLowerCase())) {
-                        mTransportManager.dumpTransportClients(pw);
-                        return;
-                    } else if ("transportstats".equals(arg.toLowerCase())) {
-                        mTransportManager.dumpTransportStats(pw);
-                        return;
-                    }
-                }
-            }
-            dumpInternal(pw);
-        } finally {
-            Binder.restoreCallingIdentity(identityToken);
-        }
-    }
-
-    private void dumpAgents(PrintWriter pw) {
-        List<PackageInfo> agentPackages = allAgentPackages();
-        pw.println("Defined backup agents:");
-        for (PackageInfo pkg : agentPackages) {
-            pw.print("  ");
-            pw.print(pkg.packageName);
-            pw.println(':');
-            pw.print("      ");
-            pw.println(pkg.applicationInfo.backupAgentName);
-        }
-    }
-
-    private void dumpInternal(PrintWriter pw) {
-        synchronized (mQueueLock) {
-            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
-                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
-                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
-            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
-            if (mBackupRunning) pw.println("Backup currently running");
-            pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
-            pw.println("Last backup pass started: " + mLastBackupPass
-                    + " (now = " + System.currentTimeMillis() + ')');
-            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
-
-            pw.println("Transport whitelist:");
-            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
-                pw.print("    ");
-                pw.println(transport.flattenToShortString());
-            }
-
-            pw.println("Available transports:");
-            final String[] transports = listAllTransports();
-            if (transports != null) {
-                for (String t : transports) {
-                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
-                            : "    ") + t);
-                    try {
-                        File dir = new File(mBaseStateDir,
-                                mTransportManager.getTransportDirName(t));
-                        pw.println("       destination: "
-                                + mTransportManager.getTransportCurrentDestinationString(t));
-                        pw.println("       intent: "
-                                + mTransportManager.getTransportConfigurationIntent(t));
-                        for (File f : dir.listFiles()) {
-                            pw.println(
-                                    "       " + f.getName() + " - " + f.length() + " state bytes");
-                        }
-                    } catch (Exception e) {
-                        Slog.e(TAG, "Error in transport", e);
-                        pw.println("        Error: " + e);
-                    }
-                }
-            }
-
-            mTransportManager.dumpTransportClients(pw);
-
-            pw.println("Pending init: " + mPendingInits.size());
-            for (String s : mPendingInits) {
-                pw.println("    " + s);
-            }
-
-            pw.print("Ancestral: ");
-            pw.println(Long.toHexString(mAncestralToken));
-            pw.print("Current:   ");
-            pw.println(Long.toHexString(mCurrentToken));
-
-            int numPackages = mBackupParticipants.size();
-            pw.println("Participants:");
-            for (int i = 0; i < numPackages; i++) {
-                int uid = mBackupParticipants.keyAt(i);
-                pw.print("  uid: ");
-                pw.println(uid);
-                HashSet<String> participants = mBackupParticipants.valueAt(i);
-                for (String app : participants) {
-                    pw.println("    " + app);
-                }
-            }
-
-            pw.println("Ancestral packages: "
-                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
-            if (mAncestralPackages != null) {
-                for (String pkg : mAncestralPackages) {
-                    pw.println("    " + pkg);
-                }
-            }
-
-            Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
-            pw.println("Ever backed up: " + processedPackages.size());
-            for (String pkg : processedPackages) {
-                pw.println("    " + pkg);
-            }
-
-            pw.println("Pending key/value backup: " + mPendingBackups.size());
-            for (BackupRequest req : mPendingBackups.values()) {
-                pw.println("    " + req);
-            }
-
-            pw.println("Full backup queue:" + mFullBackupQueue.size());
-            for (FullBackupEntry entry : mFullBackupQueue) {
-                pw.print("    ");
-                pw.print(entry.lastBackup);
-                pw.print(" : ");
-                pw.println(entry.packageName);
-            }
-        }
-    }
-
-
-    public IBackupManager getBackupManagerBinder() {
-        return mBackupManagerBinder;
+        mUserBackupManagerService.dump(fd, pw, args);
     }
 
     private static boolean backupSettingMigrated(int userId) {
@@ -3620,7 +588,7 @@
         return false;
     }
 
-    private static void writeBackupEnableState(boolean enable, int userId) {
+    static void writeBackupEnableState(boolean enable, int userId) {
         File base = new File(Environment.getDataDirectory(), "backup");
         File enableFile = new File(base, BACKUP_ENABLE_FILE);
         File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 125c225..92c2ee4 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -5,7 +5,8 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -50,7 +51,7 @@
     private static final String BACKUP_KEY_VALUE_BACKUP_DATA_FILENAME_SUFFIX = ".data";
     private static final String BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX = ".new";
 
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     private final PackageManager mPackageManager;
     private final OutputStream mOutput;
     private final PackageInfo mCurrentPackage;
@@ -66,7 +67,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
-            BackupManagerService backupManagerService, PackageManager packageManager,
+            UserBackupManagerService backupManagerService, PackageManager packageManager,
             File baseStateDir, File dataDir) {
         mOutput = output;
         mCurrentPackage = packageInfo;
@@ -85,7 +86,7 @@
         mNewStateName = new File(mStateDir,
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
-        mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
         mAgentTimeoutParameters = Preconditions.checkNotNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bb14576..bed520e 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -39,7 +39,7 @@
     private static final String TAG = "KeyValueAdbRestoreEngine";
     private static final boolean DEBUG = false;
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final File mDataDir;
 
     FileMetadata mInfo;
@@ -48,7 +48,7 @@
     IBackupAgent mAgent;
     int mToken;
 
-    public KeyValueAdbRestoreEngine(BackupManagerService backupManagerService,
+    public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
             File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
             int token) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index c805783..f2e7435 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup;
 
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+
 import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -71,7 +73,7 @@
             if (delay <= 0) {
                 delay = interval + new Random().nextInt((int) fuzz);
             }
-            if (BackupManagerService.DEBUG_SCHEDULING) {
+            if (DEBUG_SCHEDULING) {
                 Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
             }
             JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
diff --git a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
index b5db5e2..edc2379 100644
--- a/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
+++ b/services/backup/java/com/android/server/backup/ProcessedPackagesJournal.java
@@ -23,8 +23,8 @@
 import java.io.BufferedInputStream;
 import java.io.DataInputStream;
 import java.io.EOFException;
-import java.io.FileInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.util.HashSet;
@@ -46,7 +46,7 @@
 final class ProcessedPackagesJournal {
     private static final String TAG = "ProcessedPackagesJournal";
     private static final String JOURNAL_FILE_NAME = "processed";
-    private static final boolean DEBUG = BackupManagerService.DEBUG || false;
+    private static final boolean DEBUG = BackupManagerService.DEBUG;
 
     // using HashSet instead of ArraySet since we expect 100-500 elements range
     @GuardedBy("mProcessedPackages")
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 32fd7e0..59629aa 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -42,18 +42,20 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.DumpUtils;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 
 /**
- * A proxy to BackupManagerService implementation.
+ * A proxy to the {@link BackupManagerService} implementation.
  *
- * <p>This is an external interface to the BackupManagerService which is being accessed via
- * published binder (see BackupManagerService$Lifecycle). This lets us turn down the heavy
+ * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
+ * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
  * implementation object on the fly without disturbing binders that have been cached somewhere in
  * the system.
  *
@@ -143,7 +145,7 @@
     }
 
     /**
-     * Called from {@link BackupManagerService$Lifecycle} when the system user is unlocked. Attempts
+     * Called from {@link BackupManagerService.Lifecycle} when the system user is unlocked. Attempts
      * to initialize {@link BackupManagerService} and set backup state for the system user.
      *
      * @see BackupManagerService#unlockSystemUser()
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
new file mode 100644
index 0000000..fe16afe
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -0,0 +1,3501 @@
+/*
+ * 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.backup;
+
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+
+import static com.android.server.backup.BackupManagerService.DEBUG;
+import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
+import static com.android.server.backup.internal.BackupHandler.MSG_REQUEST_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
+import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AppGlobals;
+import android.app.IActivityManager;
+import android.app.IBackupAgent;
+import android.app.PendingIntent;
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.FullBackup;
+import android.app.backup.IBackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.PowerManager.ServiceType;
+import android.os.PowerSaveState;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.EventLog;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.EventLogTags;
+import com.android.server.backup.fullbackup.FullBackupEntry;
+import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.internal.Operation;
+import com.android.server.backup.internal.PerformInitializeTask;
+import com.android.server.backup.internal.ProvisionedObserver;
+import com.android.server.backup.internal.RunBackupReceiver;
+import com.android.server.backup.internal.RunInitializeReceiver;
+import com.android.server.backup.keyvalue.BackupRequest;
+import com.android.server.backup.params.AdbBackupParams;
+import com.android.server.backup.params.AdbParams;
+import com.android.server.backup.params.AdbRestoreParams;
+import com.android.server.backup.params.BackupParams;
+import com.android.server.backup.params.ClearParams;
+import com.android.server.backup.params.ClearRetryParams;
+import com.android.server.backup.params.RestoreParams;
+import com.android.server.backup.restore.ActiveRestoreSession;
+import com.android.server.backup.restore.PerformUnifiedRestoreTask;
+import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.backup.utils.AppBackupUtils;
+import com.android.server.backup.utils.BackupManagerMonitorUtils;
+import com.android.server.backup.utils.BackupObserverUtils;
+import com.android.server.backup.utils.SparseArrayUtils;
+
+import com.google.android.collect.Sets;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.RandomAccessFile;
+import java.security.SecureRandom;
+import java.text.SimpleDateFormat;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** System service that performs backup/restore operations. */
+public class UserBackupManagerService {
+    // File containing backup-enabled state.  Contains a single byte;
+    // nonzero == enabled.  File missing or contains a zero byte == disabled.
+    private static final String BACKUP_ENABLE_FILE = "backup_enabled";
+
+    // Persistently track the need to do a full init.
+    private static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
+
+    // System-private key used for backing up an app's widget state.  Must
+    // begin with U+FFxx by convention (we reserve all keys starting
+    // with U+FF00 or higher for system use).
+    public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
+    // Name and current contents version of the full-backup manifest file
+    //
+    // Manifest version history:
+    //
+    // 1 : initial release
+    public static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+    public static final int BACKUP_MANIFEST_VERSION = 1;
+
+    // External archive format version history:
+    //
+    // 1 : initial release
+    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+    // 3 : introduced "_meta" metadata file; no other format change per se
+    // 4 : added support for new device-encrypted storage locations
+    // 5 : added support for key-value packages
+    public static final int BACKUP_FILE_VERSION = 5;
+    public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
+    public static final String BACKUP_METADATA_FILENAME = "_meta";
+    public static final int BACKUP_METADATA_VERSION = 1;
+    public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
+
+    private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
+
+    // Round-robin queue for scheduling full backup passes.
+    private static final int SCHEDULE_FILE_VERSION = 1;
+
+    public static final String SETTINGS_PACKAGE = "com.android.providers.settings";
+    public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
+
+    // Pseudoname that we use for the Package Manager metadata "package".
+    public static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+
+    // Retry interval for clear/init when the transport is unavailable
+    private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
+
+    public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
+    public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
+    public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+    public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+
+    // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
+    // pending operations list.
+    public static final int OP_PENDING = 0;
+    private static final int OP_ACKNOWLEDGED = 1;
+    private static final int OP_TIMEOUT = -1;
+
+    // Waiting for backup agent to respond during backup operation.
+    public static final int OP_TYPE_BACKUP_WAIT = 0;
+
+    // Waiting for backup agent to respond during restore operation.
+    public static final int OP_TYPE_RESTORE_WAIT = 1;
+
+    // An entire backup operation spanning multiple packages.
+    public static final int OP_TYPE_BACKUP = 2;
+
+    // Time delay for initialization operations that can be delayed so as not to consume too much
+    // CPU on bring-up and increase time-to-UI.
+    private static final long INITIALIZATION_DELAY_MILLIS = 3000;
+
+    // Timeout interval for deciding that a bind or clear-data has taken too long
+    private static final long TIMEOUT_INTERVAL = 10 * 1000;
+
+    // User confirmation timeout for a full backup/restore operation.  It's this long in
+    // order to give them time to enter the backup password.
+    private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
+
+    // If an app is busy when we want to do a full-data backup, how long to defer the retry.
+    // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
+    private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
+    private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
+
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+    private final TransportManager mTransportManager;
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private IPackageManager mPackageManagerBinder;
+    private IActivityManager mActivityManager;
+    private PowerManager mPowerManager;
+    private AlarmManager mAlarmManager;
+    private IStorageManager mStorageManager;
+    private BackupManagerConstants mConstants;
+    private PowerManager.WakeLock mWakelock;
+    private BackupHandler mBackupHandler;
+
+    private IBackupManager mBackupManagerBinder;
+
+    private boolean mEnabled;   // access to this is synchronized on 'this'
+    private boolean mProvisioned;
+    private boolean mAutoRestore;
+
+    private PendingIntent mRunBackupIntent;
+    private PendingIntent mRunInitIntent;
+
+    private final ArraySet<String> mPendingInits = new ArraySet<>();  // transport names
+
+    // map UIDs to the set of participating packages under that UID
+    private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
+
+    // Backups that we haven't started yet.  Keys are package names.
+    private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+
+    // locking around the pending-backup management
+    private final Object mQueueLock = new Object();
+
+    // The thread performing the sequence of queued backups binds to each app's agent
+    // in succession.  Bind notifications are asynchronously delivered through the
+    // Activity Manager; use this lock object to signal when a requested binding has
+    // completed.
+    private final Object mAgentConnectLock = new Object();
+    private IBackupAgent mConnectedAgent;
+    private volatile boolean mConnecting;
+
+    private volatile boolean mBackupRunning;
+    private volatile long mLastBackupPass;
+
+    // A similar synchronization mechanism around clearing apps' data for restore
+    private final Object mClearDataLock = new Object();
+    private volatile boolean mClearingData;
+
+    // Used by ADB.
+    private final BackupPasswordManager mBackupPasswordManager;
+    private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>();
+    private final SecureRandom mRng = new SecureRandom();
+
+    // Time when we post the transport registration operation
+    private final long mRegisterTransportsRequestedTime;
+
+    @GuardedBy("mQueueLock")
+    private PerformFullTransportBackupTask mRunningFullBackupTask;
+
+    @GuardedBy("mQueueLock")
+    private ArrayList<FullBackupEntry> mFullBackupQueue;
+
+    @GuardedBy("mPendingRestores")
+    private boolean mIsRestoreInProgress;
+
+    @GuardedBy("mPendingRestores")
+    private final Queue<PerformUnifiedRestoreTask> mPendingRestores = new ArrayDeque<>();
+
+    private ActiveRestoreSession mActiveRestoreSession;
+
+    // Watch the device provisioning operation during setup
+    private ContentObserver mProvisionedObserver;
+
+    /**
+     * mCurrentOperations contains the list of currently active operations.
+     *
+     * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
+     * An operation wraps a BackupRestoreTask within it.
+     * It's the responsibility of this task to remove the operation from this array.
+     *
+     * A BackupRestore task gets notified of ack/timeout for the operation via
+     * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
+     * on the mCurrentOpLock.
+     * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
+     * used in various places to 'wait' for notifyAll and detect change of pending state of an
+     * operation. So typically, an operation will be removed from this array by:
+     * - BackupRestoreTask#handleCancel and
+     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+     * these places because waitUntilOperationComplete relies on the operation being present to
+     * determine its completion status.
+     *
+     * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
+     * cancel backup tasks.
+     */
+    @GuardedBy("mCurrentOpLock")
+    private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
+    private final Object mCurrentOpLock = new Object();
+    private final Random mTokenGenerator = new Random();
+    final AtomicInteger mNextToken = new AtomicInteger();
+
+    // Where we keep our journal files and other bookkeeping.
+    private File mBaseStateDir;
+    private File mDataDir;
+    private File mJournalDir;
+    @Nullable
+    private DataChangedJournal mJournal;
+    private File mFullBackupScheduleFile;
+
+    // Keep a log of all the apps we've ever backed up.
+    private ProcessedPackagesJournal mProcessedPackagesJournal;
+
+    private File mTokenFile;
+    private Set<String> mAncestralPackages = null;
+    private long mAncestralToken = 0;
+    private long mCurrentToken = 0;
+
+    @VisibleForTesting
+    public UserBackupManagerService(
+            Context context,
+            Trampoline parent,
+            HandlerThread backupThread,
+            File baseStateDir,
+            File dataDir,
+            TransportManager transportManager) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+        mPackageManagerBinder = AppGlobals.getPackageManager();
+        mActivityManager = ActivityManager.getService();
+
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
+
+        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
+        // spin up the backup/restore handler thread
+        mBackupHandler = new BackupHandler(this, backupThread.getLooper());
+
+        // Set up our bookkeeping
+        final ContentResolver resolver = context.getContentResolver();
+        mProvisioned = Settings.Global.getInt(resolver,
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        mAutoRestore = Settings.Secure.getInt(resolver,
+                Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+        mProvisionedObserver = new ProvisionedObserver(this, mBackupHandler);
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                false, mProvisionedObserver);
+
+        mBaseStateDir = baseStateDir;
+        mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
+
+        mDataDir = dataDir;
+
+        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
+
+        // Alarm receivers for scheduled backups & initialization operations
+        BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(RUN_BACKUP_ACTION);
+        context.registerReceiver(mRunBackupReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+        filter = new IntentFilter();
+        filter.addAction(RUN_INITIALIZE_ACTION);
+        context.registerReceiver(mRunInitReceiver, filter,
+                android.Manifest.permission.BACKUP, null);
+
+        Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
+        backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+
+        Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
+        initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+
+        // Set up the backup-request journaling
+        mJournalDir = new File(mBaseStateDir, "pending");
+        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
+        mJournal = null;        // will be created on first use
+
+        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+        // We are observing changes to the constants throughout the lifecycle of BMS. This is
+        // because we reference the constants in multiple areas of BMS, which otherwise would
+        // require frequent starting and stopping.
+        mConstants.start();
+
+        // Set up the various sorts of package tracking we do
+        mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
+        initPackageTracking();
+
+        // Build our mapping of uid to backup client services.  This implicitly
+        // schedules a backup pass on the Package Manager metadata the first
+        // time anything needs to be backed up.
+        synchronized (mBackupParticipants) {
+            addPackageParticipantsLocked(null);
+        }
+
+        mTransportManager = transportManager;
+        mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
+        mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
+        mBackupHandler.postDelayed(
+                mTransportManager::registerTransports, INITIALIZATION_DELAY_MILLIS);
+
+        // Now that we know about valid backup participants, parse any leftover journal files into
+        // the pending backup set
+        mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+
+        // Power management
+        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
+    }
+
+
+    public BackupManagerConstants getConstants() {
+        return mConstants;
+    }
+
+    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+        return mAgentTimeoutParameters;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    public PackageManager getPackageManager() {
+        return mPackageManager;
+    }
+
+    public void setPackageManager(PackageManager packageManager) {
+        mPackageManager = packageManager;
+    }
+
+    public IPackageManager getPackageManagerBinder() {
+        return mPackageManagerBinder;
+    }
+
+    public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
+        mPackageManagerBinder = packageManagerBinder;
+    }
+
+    public IActivityManager getActivityManager() {
+        return mActivityManager;
+    }
+
+    public void setActivityManager(IActivityManager activityManager) {
+        mActivityManager = activityManager;
+    }
+
+    public AlarmManager getAlarmManager() {
+        return mAlarmManager;
+    }
+
+    public void setAlarmManager(AlarmManager alarmManager) {
+        mAlarmManager = alarmManager;
+    }
+
+    @VisibleForTesting
+    void setPowerManager(PowerManager powerManager) {
+        mPowerManager = powerManager;
+    }
+
+    public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
+        mBackupManagerBinder = backupManagerBinder;
+    }
+
+    public TransportManager getTransportManager() {
+        return mTransportManager;
+    }
+
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    public boolean isProvisioned() {
+        return mProvisioned;
+    }
+
+    public void setProvisioned(boolean provisioned) {
+        mProvisioned = provisioned;
+    }
+
+    public PowerManager.WakeLock getWakelock() {
+        return mWakelock;
+    }
+
+    /**
+     * Sets the {@link WorkSource} of the {@link PowerManager.WakeLock} returned by {@link
+     * #getWakelock()}.
+     */
+    @VisibleForTesting
+    public void setWorkSource(@Nullable WorkSource workSource) {
+        // TODO: This is for testing, unfortunately WakeLock is final and WorkSource is not exposed
+        mWakelock.setWorkSource(workSource);
+    }
+
+    public void setWakelock(PowerManager.WakeLock wakelock) {
+        mWakelock = wakelock;
+    }
+
+    public Handler getBackupHandler() {
+        return mBackupHandler;
+    }
+
+    public void setBackupHandler(BackupHandler backupHandler) {
+        mBackupHandler = backupHandler;
+    }
+
+    public PendingIntent getRunInitIntent() {
+        return mRunInitIntent;
+    }
+
+    public void setRunInitIntent(PendingIntent runInitIntent) {
+        mRunInitIntent = runInitIntent;
+    }
+
+    public HashMap<String, BackupRequest> getPendingBackups() {
+        return mPendingBackups;
+    }
+
+    public void setPendingBackups(
+            HashMap<String, BackupRequest> pendingBackups) {
+        mPendingBackups = pendingBackups;
+    }
+
+    public Object getQueueLock() {
+        return mQueueLock;
+    }
+
+    public boolean isBackupRunning() {
+        return mBackupRunning;
+    }
+
+    public void setBackupRunning(boolean backupRunning) {
+        mBackupRunning = backupRunning;
+    }
+
+    public long getLastBackupPass() {
+        return mLastBackupPass;
+    }
+
+    public void setLastBackupPass(long lastBackupPass) {
+        mLastBackupPass = lastBackupPass;
+    }
+
+    public Object getClearDataLock() {
+        return mClearDataLock;
+    }
+
+    public boolean isClearingData() {
+        return mClearingData;
+    }
+
+    public void setClearingData(boolean clearingData) {
+        mClearingData = clearingData;
+    }
+
+    public boolean isRestoreInProgress() {
+        return mIsRestoreInProgress;
+    }
+
+    public void setRestoreInProgress(boolean restoreInProgress) {
+        mIsRestoreInProgress = restoreInProgress;
+    }
+
+    public Queue<PerformUnifiedRestoreTask> getPendingRestores() {
+        return mPendingRestores;
+    }
+
+    public ActiveRestoreSession getActiveRestoreSession() {
+        return mActiveRestoreSession;
+    }
+
+    public void setActiveRestoreSession(
+            ActiveRestoreSession activeRestoreSession) {
+        mActiveRestoreSession = activeRestoreSession;
+    }
+
+    public SparseArray<Operation> getCurrentOperations() {
+        return mCurrentOperations;
+    }
+
+    public Object getCurrentOpLock() {
+        return mCurrentOpLock;
+    }
+
+    public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
+        return mAdbBackupRestoreConfirmations;
+    }
+
+    public File getBaseStateDir() {
+        return mBaseStateDir;
+    }
+
+    public void setBaseStateDir(File baseStateDir) {
+        mBaseStateDir = baseStateDir;
+    }
+
+    public File getDataDir() {
+        return mDataDir;
+    }
+
+    public void setDataDir(File dataDir) {
+        mDataDir = dataDir;
+    }
+
+    @Nullable
+    public DataChangedJournal getJournal() {
+        return mJournal;
+    }
+
+    public void setJournal(@Nullable DataChangedJournal journal) {
+        mJournal = journal;
+    }
+
+    public SecureRandom getRng() {
+        return mRng;
+    }
+
+    public Set<String> getAncestralPackages() {
+        return mAncestralPackages;
+    }
+
+    public void setAncestralPackages(Set<String> ancestralPackages) {
+        mAncestralPackages = ancestralPackages;
+    }
+
+    public long getAncestralToken() {
+        return mAncestralToken;
+    }
+
+    public void setAncestralToken(long ancestralToken) {
+        mAncestralToken = ancestralToken;
+    }
+
+    public long getCurrentToken() {
+        return mCurrentToken;
+    }
+
+    public void setCurrentToken(long currentToken) {
+        mCurrentToken = currentToken;
+    }
+
+    public ArraySet<String> getPendingInits() {
+        return mPendingInits;
+    }
+
+    /** Clear all pending transport initializations. */
+    public void clearPendingInits() {
+        mPendingInits.clear();
+    }
+
+    public PerformFullTransportBackupTask getRunningFullBackupTask() {
+        return mRunningFullBackupTask;
+    }
+
+    public void setRunningFullBackupTask(
+            PerformFullTransportBackupTask runningFullBackupTask) {
+        mRunningFullBackupTask = runningFullBackupTask;
+    }
+
+    /**
+     *  Utility: build a new random integer token. The low bits are the ordinal of the operation for
+     *  near-time uniqueness, and the upper bits are random for app-side unpredictability.
+     */
+    public int generateRandomIntegerToken() {
+        int token = mTokenGenerator.nextInt();
+        if (token < 0) token = -token;
+        token &= ~0xFF;
+        token |= (mNextToken.incrementAndGet() & 0xFF);
+        return token;
+    }
+
+    /**
+     * Construct a backup agent instance for the metadata pseudopackage. This is a process-local
+     * non-lifecycle agent instance, so we manually set up the context topology for it.
+     */
+    public BackupAgent makeMetadataAgent() {
+        PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    /**
+     * Same as {@link #makeMetadataAgent()} but with explicit package-set configuration.
+     */
+    public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) {
+        PackageManagerBackupAgent pmAgent =
+                new PackageManagerBackupAgent(mPackageManager, packages);
+        pmAgent.attach(mContext);
+        pmAgent.onCreate();
+        return pmAgent;
+    }
+
+    private void initPackageTracking() {
+        if (MORE_DEBUG) Slog.v(TAG, "` tracking");
+
+        // Remember our ancestral dataset
+        mTokenFile = new File(mBaseStateDir, "ancestral");
+        try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream(
+                new FileInputStream(mTokenFile)))) {
+            int version = tokenStream.readInt();
+            if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
+                mAncestralToken = tokenStream.readLong();
+                mCurrentToken = tokenStream.readLong();
+
+                int numPackages = tokenStream.readInt();
+                if (numPackages >= 0) {
+                    mAncestralPackages = new HashSet<>();
+                    for (int i = 0; i < numPackages; i++) {
+                        String pkgName = tokenStream.readUTF();
+                        mAncestralPackages.add(pkgName);
+                    }
+                }
+            }
+        } catch (FileNotFoundException fnf) {
+            // Probably innocuous
+            Slog.v(TAG, "No ancestral data");
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to read token file", e);
+        }
+
+        mProcessedPackagesJournal = new ProcessedPackagesJournal(mBaseStateDir);
+        mProcessedPackagesJournal.init();
+
+        synchronized (mQueueLock) {
+            // Resume the full-data backup queue
+            mFullBackupQueue = readFullBackupSchedule();
+        }
+
+        // Register for broadcasts about package install, etc., so we can
+        // update the provider list.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+        // Register for events related to sdcard installation.
+        IntentFilter sdFilter = new IntentFilter();
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+    }
+
+    private ArrayList<FullBackupEntry> readFullBackupSchedule() {
+        boolean changed = false;
+        ArrayList<FullBackupEntry> schedule = null;
+        List<PackageInfo> apps =
+                PackageManagerBackupAgent.getStorableApplications(mPackageManager);
+
+        if (mFullBackupScheduleFile.exists()) {
+            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
+                 DataInputStream in = new DataInputStream(bufStream)) {
+                int version = in.readInt();
+                if (version != SCHEDULE_FILE_VERSION) {
+                    Slog.e(TAG, "Unknown backup schedule version " + version);
+                    return null;
+                }
+
+                final int numPackages = in.readInt();
+                schedule = new ArrayList<>(numPackages);
+
+                // HashSet instead of ArraySet specifically because we want the eventual
+                // lookups against O(hundreds) of entries to be as fast as possible, and
+                // we discard the set immediately after the scan so the extra memory
+                // overhead is transient.
+                HashSet<String> foundApps = new HashSet<>(numPackages);
+
+                for (int i = 0; i < numPackages; i++) {
+                    String pkgName = in.readUTF();
+                    long lastBackup = in.readLong();
+                    foundApps.add(pkgName); // all apps that we've addressed already
+                    try {
+                        PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0);
+                        if (AppBackupUtils.appGetsFullBackup(pkg)
+                                && AppBackupUtils.appIsEligibleForBackup(
+                                pkg.applicationInfo, mPackageManager)) {
+                            schedule.add(new FullBackupEntry(pkgName, lastBackup));
+                        } else {
+                            if (DEBUG) {
+                                Slog.i(TAG, "Package " + pkgName
+                                        + " no longer eligible for full backup");
+                            }
+                        }
+                    } catch (NameNotFoundException e) {
+                        if (DEBUG) {
+                            Slog.i(TAG, "Package " + pkgName
+                                    + " not installed; dropping from full backup");
+                        }
+                    }
+                }
+
+                // New apps can arrive "out of band" via OTA and similar, so we also need to
+                // scan to make sure that we're tracking all full-backup candidates properly
+                for (PackageInfo app : apps) {
+                    if (AppBackupUtils.appGetsFullBackup(app)
+                            && AppBackupUtils.appIsEligibleForBackup(
+                            app.applicationInfo, mPackageManager)) {
+                        if (!foundApps.contains(app.packageName)) {
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "New full backup app " + app.packageName + " found");
+                            }
+                            schedule.add(new FullBackupEntry(app.packageName, 0));
+                            changed = true;
+                        }
+                    }
+                }
+
+                Collections.sort(schedule);
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to read backup schedule", e);
+                mFullBackupScheduleFile.delete();
+                schedule = null;
+            }
+        }
+
+        if (schedule == null) {
+            // no prior queue record, or unable to read it.  Set up the queue
+            // from scratch.
+            changed = true;
+            schedule = new ArrayList<>(apps.size());
+            for (PackageInfo info : apps) {
+                if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup(
+                        info.applicationInfo, mPackageManager)) {
+                    schedule.add(new FullBackupEntry(info.packageName, 0));
+                }
+            }
+        }
+
+        if (changed) {
+            writeFullBackupScheduleAsync();
+        }
+        return schedule;
+    }
+
+    private Runnable mFullBackupScheduleWriter = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mQueueLock) {
+                try {
+                    ByteArrayOutputStream bufStream = new ByteArrayOutputStream(4096);
+                    DataOutputStream bufOut = new DataOutputStream(bufStream);
+                    bufOut.writeInt(SCHEDULE_FILE_VERSION);
+
+                    // version 1:
+                    //
+                    // [int] # of packages in the queue = N
+                    // N * {
+                    //     [utf8] package name
+                    //     [long] last backup time for this package
+                    //     }
+                    int numPackages = mFullBackupQueue.size();
+                    bufOut.writeInt(numPackages);
+
+                    for (int i = 0; i < numPackages; i++) {
+                        FullBackupEntry entry = mFullBackupQueue.get(i);
+                        bufOut.writeUTF(entry.packageName);
+                        bufOut.writeLong(entry.lastBackup);
+                    }
+                    bufOut.flush();
+
+                    AtomicFile af = new AtomicFile(mFullBackupScheduleFile);
+                    FileOutputStream out = af.startWrite();
+                    out.write(bufStream.toByteArray());
+                    af.finishWrite(out);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Unable to write backup schedule!", e);
+                }
+            }
+        }
+    };
+
+    private void writeFullBackupScheduleAsync() {
+        mBackupHandler.removeCallbacks(mFullBackupScheduleWriter);
+        mBackupHandler.post(mFullBackupScheduleWriter);
+    }
+
+    private void parseLeftoverJournals() {
+        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+        for (DataChangedJournal journal : journals) {
+            if (!journal.equals(mJournal)) {
+                try {
+                    journal.forEach(packageName -> {
+                        Slog.i(TAG, "Found stale backup journal, scheduling");
+                        if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
+                        dataChangedImpl(packageName);
+                    });
+                } catch (IOException e) {
+                    Slog.e(TAG, "Can't read " + journal, e);
+                }
+            }
+        }
+    }
+
+    /** Used for generating random salts or passwords. */
+    public byte[] randomBytes(int bits) {
+        byte[] array = new byte[bits / 8];
+        mRng.nextBytes(array);
+        return array;
+    }
+
+    /** For adb backup/restore. */
+    public boolean setBackupPassword(String currentPw, String newPw) {
+        return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
+    }
+
+    /** For adb backup/restore. */
+    public boolean hasBackupPassword() {
+        return mBackupPasswordManager.hasBackupPassword();
+    }
+
+    /** For adb backup/restore. */
+    public boolean backupPasswordMatches(String currentPw) {
+        return mBackupPasswordManager.backupPasswordMatches(currentPw);
+    }
+
+    /**
+     * Maintain persistent state around whether need to do an initialize operation. This will lock
+     * on {@link #getQueueLock()}.
+     */
+    public void recordInitPending(
+            boolean isPending, String transportName, String transportDirName) {
+        synchronized (mQueueLock) {
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "recordInitPending(" + isPending + ") on transport " + transportName);
+            }
+
+            File stateDir = new File(mBaseStateDir, transportDirName);
+            File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+
+            if (isPending) {
+                // We need an init before we can proceed with sending backup data.
+                // Record that with an entry in our set of pending inits, as well as
+                // journaling it via creation of a sentinel file.
+                mPendingInits.add(transportName);
+                try {
+                    (new FileOutputStream(initPendingFile)).close();
+                } catch (IOException ioe) {
+                    // Something is badly wrong with our permissions; just try to move on
+                }
+            } else {
+                // No more initialization needed; wipe the journal and reset our state.
+                initPendingFile.delete();
+                mPendingInits.remove(transportName);
+            }
+        }
+    }
+
+    /**
+     * Reset all of our bookkeeping because the backend data has been wiped (for example due to idle
+     * expiry), so we must re-upload all saved settings.
+     */
+    public void resetBackupState(File stateFileDir) {
+        synchronized (mQueueLock) {
+            mProcessedPackagesJournal.reset();
+
+            mCurrentToken = 0;
+            writeRestoreTokens();
+
+            // Remove all the state files
+            for (File sf : stateFileDir.listFiles()) {
+                // ... but don't touch the needs-init sentinel
+                if (!sf.getName().equals(INIT_SENTINEL_FILE_NAME)) {
+                    sf.delete();
+                }
+            }
+        }
+
+        // Enqueue a new backup of every participant
+        synchronized (mBackupParticipants) {
+            final int numParticipants = mBackupParticipants.size();
+            for (int i = 0; i < numParticipants; i++) {
+                HashSet<String> participants = mBackupParticipants.valueAt(i);
+                if (participants != null) {
+                    for (String packageName : participants) {
+                        dataChangedImpl(packageName);
+                    }
+                }
+            }
+        }
+    }
+
+    private void onTransportRegistered(String transportName, String transportDirName) {
+        if (DEBUG) {
+            long timeMs = SystemClock.elapsedRealtime() - mRegisterTransportsRequestedTime;
+            Slog.d(TAG, "Transport " + transportName + " registered " + timeMs
+                    + "ms after first request (delay = " + INITIALIZATION_DELAY_MILLIS + "ms)");
+        }
+
+        File stateDir = new File(mBaseStateDir, transportDirName);
+        stateDir.mkdirs();
+
+        File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+        if (initSentinel.exists()) {
+            synchronized (mQueueLock) {
+                mPendingInits.add(transportName);
+
+                // TODO: pick a better starting time than now + 1 minute
+                long delay = 1000 * 60; // one minute, in milliseconds
+                mAlarmManager.set(AlarmManager.RTC_WAKEUP,
+                        System.currentTimeMillis() + delay, mRunInitIntent);
+            }
+        }
+    }
+
+    // ----- Track installation/removal of packages -----
+    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+
+            String action = intent.getAction();
+            boolean replacing = false;
+            boolean added = false;
+            boolean changed = false;
+            Bundle extras = intent.getExtras();
+            String[] pkgList = null;
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                Uri uri = intent.getData();
+                if (uri == null) {
+                    return;
+                }
+                final String pkgName = uri.getSchemeSpecificPart();
+                if (pkgName != null) {
+                    pkgList = new String[]{pkgName};
+                }
+                changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
+
+                // At package-changed we only care about looking at new transport states
+                if (changed) {
+                    final String[] components =
+                            intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+                        for (int i = 0; i < components.length; i++) {
+                            Slog.i(TAG, "   * " + components[i]);
+                        }
+                    }
+
+                    mBackupHandler.post(
+                            () -> mTransportManager.onPackageChanged(pkgName, components));
+                    return; // nothing more to do in the PACKAGE_CHANGED case
+                }
+
+                added = Intent.ACTION_PACKAGE_ADDED.equals(action);
+                replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                added = true;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                added = false;
+                pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            }
+
+            if (pkgList == null || pkgList.length == 0) {
+                return;
+            }
+
+            final int uid = extras.getInt(Intent.EXTRA_UID);
+            if (added) {
+                synchronized (mBackupParticipants) {
+                    if (replacing) {
+                        // This is the package-replaced case; we just remove the entry
+                        // under the old uid and fall through to re-add.  If an app
+                        // just added key/value backup participation, this picks it up
+                        // as a known participant.
+                        removePackageParticipantsLocked(pkgList, uid);
+                    }
+                    addPackageParticipantsLocked(pkgList);
+                }
+                // If they're full-backup candidates, add them there instead
+                final long now = System.currentTimeMillis();
+                for (final String packageName : pkgList) {
+                    try {
+                        PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+                        if (AppBackupUtils.appGetsFullBackup(app)
+                                && AppBackupUtils.appIsEligibleForBackup(
+                                app.applicationInfo, mPackageManager)) {
+                            enqueueFullBackup(packageName, now);
+                            scheduleNextFullBackupJob(0);
+                        } else {
+                            // The app might have just transitioned out of full-data into
+                            // doing key/value backups, or might have just disabled backups
+                            // entirely.  Make sure it is no longer in the full-data queue.
+                            synchronized (mQueueLock) {
+                                dequeueFullBackupLocked(packageName);
+                            }
+                            writeFullBackupScheduleAsync();
+                        }
+
+                        mBackupHandler.post(
+                                () -> mTransportManager.onPackageAdded(packageName));
+
+                    } catch (NameNotFoundException e) {
+                        // doesn't really exist; ignore it
+                        if (DEBUG) {
+                            Slog.w(TAG, "Can't resolve new app " + packageName);
+                        }
+                    }
+                }
+
+                // Whenever a package is added or updated we need to update
+                // the package metadata bookkeeping.
+                dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
+            } else {
+                if (replacing) {
+                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
+                } else {
+                    // Outright removal.  In the full-data case, the app will be dropped
+                    // from the queue when its (now obsolete) name comes up again for
+                    // backup.
+                    synchronized (mBackupParticipants) {
+                        removePackageParticipantsLocked(pkgList, uid);
+                    }
+                }
+                for (final String pkgName : pkgList) {
+                    mBackupHandler.post(
+                            () -> mTransportManager.onPackageRemoved(pkgName));
+                }
+            }
+        }
+    };
+
+    // Add the backup agents in the given packages to our set of known backup participants.
+    // If 'packageNames' is null, adds all backup agents in the whole system.
+    private void addPackageParticipantsLocked(String[] packageNames) {
+        // Look for apps that define the android:backupAgent attribute
+        List<PackageInfo> targetApps = allAgentPackages();
+        if (packageNames != null) {
+            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: #" + packageNames.length);
+            for (String packageName : packageNames) {
+                addPackageParticipantsLockedInner(packageName, targetApps);
+            }
+        } else {
+            if (MORE_DEBUG) Slog.v(TAG, "addPackageParticipantsLocked: all");
+            addPackageParticipantsLockedInner(null, targetApps);
+        }
+    }
+
+    private void addPackageParticipantsLockedInner(String packageName,
+            List<PackageInfo> targetPkgs) {
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "Examining " + packageName + " for backup agent");
+        }
+
+        for (PackageInfo pkg : targetPkgs) {
+            if (packageName == null || pkg.packageName.equals(packageName)) {
+                int uid = pkg.applicationInfo.uid;
+                HashSet<String> set = mBackupParticipants.get(uid);
+                if (set == null) {
+                    set = new HashSet<>();
+                    mBackupParticipants.put(uid, set);
+                }
+                set.add(pkg.packageName);
+                if (MORE_DEBUG) Slog.v(TAG, "Agent found; added");
+
+                // Schedule a backup for it on general principles
+                if (MORE_DEBUG) Slog.i(TAG, "Scheduling backup for new app " + pkg.packageName);
+                Message msg = mBackupHandler
+                        .obtainMessage(MSG_SCHEDULE_BACKUP_PACKAGE, pkg.packageName);
+                mBackupHandler.sendMessage(msg);
+            }
+        }
+    }
+
+    // Remove the given packages' entries from our known active set.
+    private void removePackageParticipantsLocked(String[] packageNames, int oldUid) {
+        if (packageNames == null) {
+            Slog.w(TAG, "removePackageParticipants with null list");
+            return;
+        }
+
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "removePackageParticipantsLocked: uid=" + oldUid
+                    + " #" + packageNames.length);
+        }
+        for (String pkg : packageNames) {
+            // Known previous UID, so we know which package set to check
+            HashSet<String> set = mBackupParticipants.get(oldUid);
+            if (set != null && set.contains(pkg)) {
+                removePackageFromSetLocked(set, pkg);
+                if (set.isEmpty()) {
+                    if (MORE_DEBUG) Slog.v(TAG, "  last one of this uid; purging set");
+                    mBackupParticipants.remove(oldUid);
+                }
+            }
+        }
+    }
+
+    private void removePackageFromSetLocked(final HashSet<String> set,
+            final String packageName) {
+        if (set.contains(packageName)) {
+            // Found it.  Remove this one package from the bookkeeping, and
+            // if it's the last participating app under this uid we drop the
+            // (now-empty) set as well.
+            // Note that we deliberately leave it 'known' in the "ever backed up"
+            // bookkeeping so that its current-dataset data will be retrieved
+            // if the app is subsequently reinstalled
+            if (MORE_DEBUG) Slog.v(TAG, "  removing participant " + packageName);
+            set.remove(packageName);
+            mPendingBackups.remove(packageName);
+        }
+    }
+
+    // Returns the set of all applications that define an android:backupAgent attribute
+    private List<PackageInfo> allAgentPackages() {
+        // !!! TODO: cache this and regenerate only when necessary
+        int flags = PackageManager.GET_SIGNING_CERTIFICATES;
+        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+        int numPackages = packages.size();
+        for (int a = numPackages - 1; a >= 0; a--) {
+            PackageInfo pkg = packages.get(a);
+            try {
+                ApplicationInfo app = pkg.applicationInfo;
+                if (((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+                        || app.backupAgentName == null
+                        || (app.flags & ApplicationInfo.FLAG_FULL_BACKUP_ONLY) != 0) {
+                    packages.remove(a);
+                } else {
+                    // we will need the shared library path, so look that up and store it here.
+                    // This is used implicitly when we pass the PackageInfo object off to
+                    // the Activity Manager to launch the app for backup/restore purposes.
+                    app = mPackageManager.getApplicationInfo(pkg.packageName,
+                            PackageManager.GET_SHARED_LIBRARY_FILES);
+                    pkg.applicationInfo.sharedLibraryFiles = app.sharedLibraryFiles;
+                    pkg.applicationInfo.sharedLibraryInfos = app.sharedLibraryInfos;
+                }
+            } catch (NameNotFoundException e) {
+                packages.remove(a);
+            }
+        }
+        return packages;
+    }
+
+    /**
+     * Called from the backup tasks: record that the given app has been successfully backed up at
+     * least once. This includes both key/value and full-data backups through the transport.
+     */
+    public void logBackupComplete(String packageName) {
+        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+        for (String receiver : mConstants.getBackupFinishedNotificationReceivers()) {
+            final Intent notification = new Intent();
+            notification.setAction(BACKUP_FINISHED_ACTION);
+            notification.setPackage(receiver);
+            notification.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES
+                    | Intent.FLAG_RECEIVER_FOREGROUND);
+            notification.putExtra(BACKUP_FINISHED_PACKAGE_EXTRA, packageName);
+            mContext.sendBroadcastAsUser(notification, UserHandle.OWNER);
+        }
+
+        mProcessedPackagesJournal.addPackage(packageName);
+    }
+
+    /**
+     * Persistently record the current and ancestral backup tokens, as well as the set of packages
+     * with data available in the ancestral dataset.
+     */
+    public void writeRestoreTokens() {
+        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
+            // First, the version number of this record, for futureproofing
+            af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
+
+            // Write the ancestral and current tokens
+            af.writeLong(mAncestralToken);
+            af.writeLong(mCurrentToken);
+
+            // Now write the set of ancestral packages
+            if (mAncestralPackages == null) {
+                af.writeInt(-1);
+            } else {
+                af.writeInt(mAncestralPackages.size());
+                if (DEBUG) Slog.v(TAG, "Ancestral packages:  " + mAncestralPackages.size());
+                for (String pkgName : mAncestralPackages) {
+                    af.writeUTF(pkgName);
+                    if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
+                }
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to write token file:", e);
+        }
+    }
+
+    /** Fires off a backup agent, blocking until it attaches or times out. */
+    @Nullable
+    public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
+        IBackupAgent agent = null;
+        synchronized (mAgentConnectLock) {
+            mConnecting = true;
+            mConnectedAgent = null;
+            try {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode,
+                        UserHandle.USER_OWNER)) {
+                    Slog.d(TAG, "awaiting agent for " + app);
+
+                    // success; wait for the agent to arrive
+                    // only wait 10 seconds for the bind to happen
+                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+                    while (mConnecting && mConnectedAgent == null
+                            && (System.currentTimeMillis() < timeoutMark)) {
+                        try {
+                            mAgentConnectLock.wait(5000);
+                        } catch (InterruptedException e) {
+                            // just bail
+                            Slog.w(TAG, "Interrupted: " + e);
+                            mConnecting = false;
+                            mConnectedAgent = null;
+                        }
+                    }
+
+                    // if we timed out with no connect, abort and move on
+                    if (mConnecting) {
+                        Slog.w(TAG, "Timeout waiting for agent " + app);
+                        mConnectedAgent = null;
+                    }
+                    if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
+                    agent = mConnectedAgent;
+                }
+            } catch (RemoteException e) {
+                // can't happen - ActivityManager is local
+            }
+        }
+        if (agent == null) {
+            try {
+                mActivityManager.clearPendingBackup();
+            } catch (RemoteException e) {
+                // can't happen - ActivityManager is local
+            }
+        }
+        return agent;
+    }
+
+    /** Unbind from a backup agent. */
+    public void unbindAgent(ApplicationInfo app) {
+        try {
+            mActivityManager.unbindBackupAgent(app);
+        } catch (RemoteException e) {
+            // Can't happen - activity manager is local
+        }
+    }
+
+    /**
+     * Clear an application's data, blocking until the operation completes or times out. If {@code
+     * keepSystemState} is {@code true}, we intentionally do not clear system state that would
+     * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're
+     * bringing it into the actual expected state related to the already-restored notification state
+     * etc.
+     */
+    public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) {
+        // Don't wipe packages marked allowClearUserData=false
+        try {
+            PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+            if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "allowClearUserData=false so not wiping "
+                            + packageName);
+                }
+                return;
+            }
+        } catch (NameNotFoundException e) {
+            Slog.w(TAG, "Tried to clear data for " + packageName + " but not found");
+            return;
+        }
+
+        ClearDataObserver observer = new ClearDataObserver(this);
+
+        synchronized (mClearDataLock) {
+            mClearingData = true;
+            try {
+                mActivityManager.clearApplicationUserData(
+                        packageName, keepSystemState, observer, 0);
+            } catch (RemoteException e) {
+                // can't happen because the activity manager is in this process
+            }
+
+            // only wait 10 seconds for the clear data to happen
+            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+                try {
+                    mClearDataLock.wait(5000);
+                } catch (InterruptedException e) {
+                    // won't happen, but still.
+                    mClearingData = false;
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(String packageName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getAvailableRestoreToken");
+
+        long token = mAncestralToken;
+        synchronized (mQueueLock) {
+            if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "App in ever-stored, so using current token");
+                }
+                token = mCurrentToken;
+            }
+        }
+        if (MORE_DEBUG) Slog.i(TAG, "getAvailableRestoreToken() == " + token);
+        return token;
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages}.
+     *
+     * @see #requestBackup(String[], IBackupObserver, IBackupManagerMonitor, int).
+     */
+    public int requestBackup(String[] packages, IBackupObserver observer, int flags) {
+        return requestBackup(packages, observer, null, flags);
+    }
+
+    /**
+     * Requests a backup for the inputted {@code packages} with a specified {@link
+     * IBackupManagerMonitor}.
+     */
+    public int requestBackup(String[] packages, IBackupObserver observer,
+            IBackupManagerMonitor monitor, int flags) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "requestBackup");
+
+        if (packages == null || packages.length < 1) {
+            Slog.e(TAG, "No packages named for backup request");
+            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_NO_PACKAGES,
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+            throw new IllegalArgumentException("No packages are provided for backup");
+        }
+
+        if (!mEnabled || !mProvisioned) {
+            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+            BackupObserverUtils.sendBackupFinished(observer,
+                    BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+            final int logTag = mProvisioned
+                    ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
+                    : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor, logTag, null,
+                    BackupManagerMonitor.LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY, null);
+            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+        }
+
+        final TransportClient transportClient;
+        final String transportDirName;
+        try {
+            transportDirName =
+                    mTransportManager.getTransportDirName(
+                            mTransportManager.getCurrentTransportName());
+            transportClient =
+                    mTransportManager.getCurrentTransportClientOrThrow("BMS.requestBackup()");
+        } catch (TransportNotRegisteredException e) {
+            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
+            monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
+                    BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
+                    null, BackupManagerMonitor.LOG_EVENT_CATEGORY_TRANSPORT, null);
+            return BackupManager.ERROR_TRANSPORT_ABORTED;
+        }
+
+        OnTaskFinishedListener listener =
+                caller -> mTransportManager.disposeOfTransportClient(transportClient, caller);
+
+        ArrayList<String> fullBackupList = new ArrayList<>();
+        ArrayList<String> kvBackupList = new ArrayList<>();
+        for (String packageName : packages) {
+            if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+                kvBackupList.add(packageName);
+                continue;
+            }
+            try {
+                PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNING_CERTIFICATES);
+                if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo,
+                        mPackageManager)) {
+                    BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+                            BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                    continue;
+                }
+                if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+                    fullBackupList.add(packageInfo.packageName);
+                } else {
+                    kvBackupList.add(packageInfo.packageName);
+                }
+            } catch (NameNotFoundException e) {
+                BackupObserverUtils.sendBackupOnPackageResult(observer, packageName,
+                        BackupManager.ERROR_PACKAGE_NOT_FOUND);
+            }
+        }
+        EventLog.writeEvent(EventLogTags.BACKUP_REQUESTED, packages.length, kvBackupList.size(),
+                fullBackupList.size());
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Backup requested for " + packages.length + " packages, of them: "
+                    + fullBackupList.size() + " full backups, " + kvBackupList.size()
+                    + " k/v backups");
+        }
+
+        boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
+
+        Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
+        msg.obj = new BackupParams(transportClient, transportDirName, kvBackupList, fullBackupList,
+                observer, monitor, listener, true, nonIncrementalBackup);
+        mBackupHandler.sendMessage(msg);
+        return BackupManager.SUCCESS;
+    }
+
+    /** Cancel all running backups. */
+    public void cancelBackups() {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "cancelBackups");
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "cancelBackups() called.");
+        }
+        final long oldToken = Binder.clearCallingIdentity();
+        try {
+            List<Integer> operationsToCancel = new ArrayList<>();
+            synchronized (mCurrentOpLock) {
+                for (int i = 0; i < mCurrentOperations.size(); i++) {
+                    Operation op = mCurrentOperations.valueAt(i);
+                    int token = mCurrentOperations.keyAt(i);
+                    if (op.type == OP_TYPE_BACKUP) {
+                        operationsToCancel.add(token);
+                    }
+                }
+            }
+            for (Integer token : operationsToCancel) {
+                handleCancel(token, true /* cancelAll */);
+            }
+            // We don't want the backup jobs to kick in any time soon.
+            // Reschedules them to run in the distant future.
+            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Schedule a timeout message for the operation identified by {@code token}. */
+    public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
+            int operationType) {
+        if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+            Slog.wtf(TAG, "prepareOperationTimeout() doesn't support operation "
+                    + Integer.toHexString(token) + " of type " + operationType);
+            return;
+        }
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+                    + " interval=" + interval + " callback=" + callback);
+        }
+
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
+            Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+                    token, 0, callback);
+            mBackupHandler.sendMessageDelayed(msg, interval);
+        }
+    }
+
+    private int getMessageIdForOperationType(int operationType) {
+        switch (operationType) {
+            case OP_TYPE_BACKUP_WAIT:
+                return MSG_BACKUP_OPERATION_TIMEOUT;
+            case OP_TYPE_RESTORE_WAIT:
+                return MSG_RESTORE_OPERATION_TIMEOUT;
+            default:
+                Slog.wtf(TAG, "getMessageIdForOperationType called on invalid operation type: "
+                        + operationType);
+                return -1;
+        }
+    }
+
+    /**
+     * Add an operation to the list of currently running operations. Used for cancellation,
+     * completion and timeout callbacks that act on the operation via the {@code token}.
+     */
+    public void putOperation(int token, Operation operation) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Adding operation token=" + Integer.toHexString(token) + ", operation type="
+                    + operation.type);
+        }
+        synchronized (mCurrentOpLock) {
+            mCurrentOperations.put(token, operation);
+        }
+    }
+
+    /**
+     * Remove an operation from the list of currently running operations. An operation is removed
+     * when it is completed, cancelled, or timed out, and thus no longer running.
+     */
+    public void removeOperation(int token) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Removing operation token=" + Integer.toHexString(token));
+        }
+        synchronized (mCurrentOpLock) {
+            if (mCurrentOperations.get(token) == null) {
+                Slog.w(TAG, "Duplicate remove for operation. token="
+                        + Integer.toHexString(token));
+            }
+            mCurrentOperations.remove(token);
+        }
+    }
+
+    /** Block until we received an operation complete message (from the agent or cancellation). */
+    public boolean waitUntilOperationComplete(int token) {
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Blocking until operation complete for "
+                    + Integer.toHexString(token));
+        }
+        int finalState = OP_PENDING;
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            while (true) {
+                op = mCurrentOperations.get(token);
+                if (op == null) {
+                    // mysterious disappearance: treat as success with no callback
+                    break;
+                } else {
+                    if (op.state == OP_PENDING) {
+                        try {
+                            mCurrentOpLock.wait();
+                        } catch (InterruptedException e) {
+                        }
+                        // When the wait is notified we loop around and recheck the current state
+                    } else {
+                        if (MORE_DEBUG) {
+                            Slog.d(TAG, "Unblocked waiting for operation token="
+                                    + Integer.toHexString(token));
+                        }
+                        // No longer pending; we're done
+                        finalState = op.state;
+                        break;
+                    }
+                }
+            }
+        }
+
+        removeOperation(token);
+        if (op != null) {
+            mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+        }
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "operation " + Integer.toHexString(token)
+                    + " complete: finalState=" + finalState);
+        }
+        return finalState == OP_ACKNOWLEDGED;
+    }
+
+    /** Cancel the operation associated with {@code token}. */
+    public void handleCancel(int token, boolean cancelAll) {
+        // Notify any synchronous waiters
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            op = mCurrentOperations.get(token);
+            if (MORE_DEBUG) {
+                if (op == null) {
+                    Slog.w(TAG, "Cancel of token " + Integer.toHexString(token)
+                            + " but no op found");
+                }
+            }
+            int state = (op != null) ? op.state : OP_TIMEOUT;
+            if (state == OP_ACKNOWLEDGED) {
+                // The operation finished cleanly, so we have nothing more to do.
+                if (DEBUG) {
+                    Slog.w(TAG, "Operation already got an ack."
+                            + "Should have been removed from mCurrentOperations.");
+                }
+                op = null;
+                mCurrentOperations.delete(token);
+            } else if (state == OP_PENDING) {
+                if (DEBUG) Slog.v(TAG, "Cancel: token=" + Integer.toHexString(token));
+                op.state = OP_TIMEOUT;
+                // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
+                // called after we receive cancel here. We need this op's state there.
+
+                // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
+                // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+                // doesn't require cancellation.
+                if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
+                    mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
+                }
+            }
+            mCurrentOpLock.notifyAll();
+        }
+
+        // If there's a TimeoutHandler for this event, call it
+        if (op != null && op.callback != null) {
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "   Invoking cancel on " + op.callback);
+            }
+            op.callback.handleCancel(cancelAll);
+        }
+    }
+
+    /** Returns {@code true} if a backup is currently running, else returns {@code false}. */
+    public boolean isBackupOperationInProgress() {
+        synchronized (mCurrentOpLock) {
+            for (int i = 0; i < mCurrentOperations.size(); i++) {
+                Operation op = mCurrentOperations.valueAt(i);
+                if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /** Unbind the backup agent and kill the app if it's a non-system app. */
+    public void tearDownAgentAndKill(ApplicationInfo app) {
+        if (app == null) {
+            // Null means the system package, so just quietly move on.  :)
+            return;
+        }
+
+        try {
+            // unbind and tidy up even on timeout or failure, just in case
+            mActivityManager.unbindBackupAgent(app);
+
+            // The agent was running with a stub Application object, so shut it down.
+            // !!! We hardcode the confirmation UI's package name here rather than use a
+            //     manifest flag!  TODO something less direct.
+            if (app.uid >= Process.FIRST_APPLICATION_UID
+                    && !app.packageName.equals("com.android.backupconfirm")) {
+                if (MORE_DEBUG) Slog.d(TAG, "Killing agent host process");
+                mActivityManager.killApplicationProcess(app.processName, app.uid);
+            } else {
+                if (MORE_DEBUG) Slog.d(TAG, "Not killing after operation: " + app.processName);
+            }
+        } catch (RemoteException e) {
+            Slog.d(TAG, "Lost app trying to shut down");
+        }
+    }
+
+    /** For adb backup/restore. */
+    public boolean deviceIsEncrypted() {
+        try {
+            return mStorageManager.getEncryptionState()
+                    != StorageManager.ENCRYPTION_STATE_NONE
+                    && mStorageManager.getPasswordType()
+                    != StorageManager.CRYPT_TYPE_DEFAULT;
+        } catch (Exception e) {
+            // If we can't talk to the storagemanager service we have a serious problem; fail
+            // "secure" i.e. assuming that the device is encrypted.
+            Slog.e(TAG, "Unable to communicate with storagemanager service: " + e.getMessage());
+            return true;
+        }
+    }
+
+    // ----- Full-data backup scheduling -----
+
+    /**
+     * Schedule a job to tell us when it's a good time to run a full backup
+     */
+    public void scheduleNextFullBackupJob(long transportMinLatency) {
+        synchronized (mQueueLock) {
+            if (mFullBackupQueue.size() > 0) {
+                // schedule the next job at the point in the future when the least-recently
+                // backed up app comes due for backup again; or immediately if it's already
+                // due.
+                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+                final long interval = mConstants.getFullBackupIntervalMilliseconds();
+                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
+                final long latency = Math.max(transportMinLatency, appLatency);
+                Runnable r = new Runnable() {
+                    @Override
+                    public void run() {
+                        FullBackupJob.schedule(mContext, latency, mConstants);
+                    }
+                };
+                mBackupHandler.postDelayed(r, 2500);
+            } else {
+                if (DEBUG_SCHEDULING) {
+                    Slog.i(TAG, "Full backup queue empty; not scheduling");
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove a package from the full-data queue.
+     */
+    @GuardedBy("mQueueLock")
+    private void dequeueFullBackupLocked(String packageName) {
+        final int numPackages = mFullBackupQueue.size();
+        for (int i = numPackages - 1; i >= 0; i--) {
+            final FullBackupEntry e = mFullBackupQueue.get(i);
+            if (packageName.equals(e.packageName)) {
+                mFullBackupQueue.remove(i);
+            }
+        }
+    }
+
+    /**
+     * Enqueue full backup for the given app, with a note about when it last ran.
+     */
+    public void enqueueFullBackup(String packageName, long lastBackedUp) {
+        FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
+        synchronized (mQueueLock) {
+            // First, sanity check that we aren't adding a duplicate.  Slow but
+            // straightforward; we'll have at most on the order of a few hundred
+            // items in this list.
+            dequeueFullBackupLocked(packageName);
+
+            // This is also slow but easy for modest numbers of apps: work backwards
+            // from the end of the queue until we find an item whose last backup
+            // time was before this one, then insert this new entry after it.  If we're
+            // adding something new we don't bother scanning, and just prepend.
+            int which = -1;
+            if (lastBackedUp > 0) {
+                for (which = mFullBackupQueue.size() - 1; which >= 0; which--) {
+                    final FullBackupEntry entry = mFullBackupQueue.get(which);
+                    if (entry.lastBackup <= lastBackedUp) {
+                        mFullBackupQueue.add(which + 1, newEntry);
+                        break;
+                    }
+                }
+            }
+            if (which < 0) {
+                // this one is earlier than any existing one, so prepend
+                mFullBackupQueue.add(0, newEntry);
+            }
+        }
+        writeFullBackupScheduleAsync();
+    }
+
+    private boolean fullBackupAllowable(String transportName) {
+        if (!mTransportManager.isTransportRegistered(transportName)) {
+            Slog.w(TAG, "Transport not registered; full data backup not performed");
+            return false;
+        }
+
+        // Don't proceed unless we have already established package metadata
+        // for the current dataset via a key/value backup pass.
+        try {
+            String transportDirName = mTransportManager.getTransportDirName(transportName);
+            File stateDir = new File(mBaseStateDir, transportDirName);
+            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+            if (pmState.length() <= 0) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+                }
+                return false;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to get transport name: " + e.getMessage());
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Conditions are right for a full backup operation, so run one.  The model we use is
+     * to perform one app backup per scheduled job execution, and to reschedule the job
+     * with zero latency as long as conditions remain right and we still have work to do.
+     *
+     * <p>This is the "start a full backup operation" entry point called by the scheduled job.
+     *
+     * @return Whether ongoing work will continue.  The return value here will be passed
+     * along as the return value to the scheduled job's onStartJob() callback.
+     */
+    public boolean beginFullBackup(FullBackupJob scheduledJob) {
+        final long now = System.currentTimeMillis();
+        final long fullBackupInterval;
+        final long keyValueBackupInterval;
+        synchronized (mConstants) {
+            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+        }
+        FullBackupEntry entry = null;
+        long latency = fullBackupInterval;
+
+        if (!mEnabled || !mProvisioned) {
+            // Backups are globally disabled, so don't proceed.  We also don't reschedule
+            // the job driving automatic backups; that job will be scheduled again when
+            // the user enables backup.
+            if (MORE_DEBUG) {
+                Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+                        + " p=" + mProvisioned + "; ignoring");
+            }
+            return false;
+        }
+
+        // Don't run the backup if we're in battery saver mode, but reschedule
+        // to try again in the not-so-distant future.
+        final PowerSaveState result =
+                mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
+        if (result.batterySaverEnabled) {
+            if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
+            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
+            return false;
+        }
+
+        if (DEBUG_SCHEDULING) {
+            Slog.i(TAG, "Beginning scheduled full backup operation");
+        }
+
+        // Great; we're able to run full backup jobs now.  See if we have any work to do.
+        synchronized (mQueueLock) {
+            if (mRunningFullBackupTask != null) {
+                Slog.e(TAG, "Backup triggered but one already/still running!");
+                return false;
+            }
+
+            // At this point we think that we have work to do, but possibly not right now.
+            // Any exit without actually running backups will also require that we
+            // reschedule the job.
+            boolean runBackup = true;
+            boolean headBusy;
+
+            do {
+                // Recheck each time, because culling due to ineligibility may
+                // have emptied the queue.
+                if (mFullBackupQueue.size() == 0) {
+                    // no work to do so just bow out
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup queue empty; doing nothing");
+                    }
+                    runBackup = false;
+                    break;
+                }
+
+                headBusy = false;
+
+                String transportName = mTransportManager.getCurrentTransportName();
+                if (!fullBackupAllowable(transportName)) {
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Preconditions not met; not running full backup");
+                    }
+                    runBackup = false;
+                    // Typically this means we haven't run a key/value backup yet.  Back off
+                    // full-backup operations by the key/value job's run interval so that
+                    // next time we run, we are likely to be able to make progress.
+                    latency = keyValueBackupInterval;
+                }
+
+                if (runBackup) {
+                    entry = mFullBackupQueue.get(0);
+                    long timeSinceRun = now - entry.lastBackup;
+                    runBackup = (timeSinceRun >= fullBackupInterval);
+                    if (!runBackup) {
+                        // It's too early to back up the next thing in the queue, so bow out
+                        if (MORE_DEBUG) {
+                            Slog.i(TAG, "Device ready but too early to back up next app");
+                        }
+                        // Wait until the next app in the queue falls due for a full data backup
+                        latency = fullBackupInterval - timeSinceRun;
+                        break;  // we know we aren't doing work yet, so bail.
+                    }
+
+                    try {
+                        PackageInfo appInfo = mPackageManager.getPackageInfo(entry.packageName, 0);
+                        if (!AppBackupUtils.appGetsFullBackup(appInfo)) {
+                            // The head app isn't supposed to get full-data backups [any more];
+                            // so we cull it and force a loop around to consider the new head
+                            // app.
+                            if (MORE_DEBUG) {
+                                Slog.i(TAG, "Culling package " + entry.packageName
+                                        + " in full-backup queue but not eligible");
+                            }
+                            mFullBackupQueue.remove(0);
+                            headBusy = true; // force the while() condition
+                            continue;
+                        }
+
+                        final int privFlags = appInfo.applicationInfo.privateFlags;
+                        headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
+                                && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+
+                        if (headBusy) {
+                            final long nextEligible = System.currentTimeMillis()
+                                    + BUSY_BACKOFF_MIN_MILLIS
+                                    + mTokenGenerator.nextInt(BUSY_BACKOFF_FUZZ);
+                            if (DEBUG_SCHEDULING) {
+                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                                Slog.i(TAG, "Full backup time but " + entry.packageName
+                                        + " is busy; deferring to "
+                                        + sdf.format(new Date(nextEligible)));
+                            }
+                            // This relocates the app's entry from the head of the queue to
+                            // its order-appropriate position further down, so upon looping
+                            // a new candidate will be considered at the head.
+                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
+                        }
+                    } catch (NameNotFoundException nnf) {
+                        // So, we think we want to back this up, but it turns out the package
+                        // in question is no longer installed.  We want to drop it from the
+                        // queue entirely and move on, but if there's nothing else in the queue
+                        // we should bail entirely.  headBusy cannot have been set to true yet.
+                        runBackup = (mFullBackupQueue.size() > 1);
+                    } catch (RemoteException e) {
+                        // Cannot happen; the Activity Manager is in the same process
+                    }
+                }
+            } while (headBusy);
+
+            if (!runBackup) {
+                if (DEBUG_SCHEDULING) {
+                    Slog.i(TAG, "Nothing pending full backup; rescheduling +" + latency);
+                }
+                final long deferTime = latency;     // pin for the closure
+                mBackupHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        FullBackupJob.schedule(mContext, deferTime, mConstants);
+                    }
+                });
+                return false;
+            }
+
+            // Okay, the top thing is ready for backup now.  Do it.
+            mFullBackupQueue.remove(0);
+            CountDownLatch latch = new CountDownLatch(1);
+            String[] pkg = new String[]{entry.packageName};
+            mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
+                    this,
+                    /* observer */ null,
+                    pkg,
+                    /* updateSchedule */ true,
+                    scheduledJob,
+                    latch,
+                    /* backupObserver */ null,
+                    /* monitor */ null,
+                    /* userInitiated */ false,
+                    "BMS.beginFullBackup()");
+            // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+            mWakelock.acquire();
+            (new Thread(mRunningFullBackupTask)).start();
+        }
+
+        return true;
+    }
+
+    /**
+     * The job scheduler says our constraints don't hold anymore, so tear down any ongoing backup
+     * task right away.
+     */
+    public void endFullBackup() {
+        // offload the mRunningFullBackupTask.handleCancel() call to another thread,
+        // as we might have to wait for mCancelLock
+        Runnable endFullBackupRunnable = new Runnable() {
+            @Override
+            public void run() {
+                PerformFullTransportBackupTask pftbt = null;
+                synchronized (mQueueLock) {
+                    if (mRunningFullBackupTask != null) {
+                        pftbt = mRunningFullBackupTask;
+                    }
+                }
+                if (pftbt != null) {
+                    if (DEBUG_SCHEDULING) {
+                        Slog.i(TAG, "Telling running backup to stop");
+                    }
+                    pftbt.handleCancel(true);
+                }
+            }
+        };
+        new Thread(endFullBackupRunnable, "end-full-backup").start();
+    }
+
+    /** Used by both incremental and full restore to restore widget data. */
+    public void restoreWidgetData(String packageName, byte[] widgetData) {
+        // Apply the restored widget state and generate the ID update for the app
+        // TODO: http://b/22388012
+        if (MORE_DEBUG) {
+            Slog.i(TAG, "Incorporating restored widget data");
+        }
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_SYSTEM);
+    }
+
+    // *****************************
+    // NEW UNIFIED RESTORE IMPLEMENTATION
+    // *****************************
+
+    /** Schedule a backup pass for {@code packageName}. */
+    public void dataChangedImpl(String packageName) {
+        HashSet<String> targets = dataChangedTargets(packageName);
+        dataChangedImpl(packageName, targets);
+    }
+
+    private void dataChangedImpl(String packageName, HashSet<String> targets) {
+        // Record that we need a backup pass for the caller.  Since multiple callers
+        // may share a uid, we need to note all candidates within that uid and schedule
+        // a backup pass for each of them.
+        if (targets == null) {
+            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+                    + " uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        synchronized (mQueueLock) {
+            // Note that this client has made data changes that need to be backed up
+            if (targets.contains(packageName)) {
+                // Add the caller to the set of pending backups.  If there is
+                // one already there, then overwrite it, but no harm done.
+                BackupRequest req = new BackupRequest(packageName);
+                if (mPendingBackups.put(packageName, req) == null) {
+                    if (MORE_DEBUG) Slog.d(TAG, "Now staging backup of " + packageName);
+
+                    // Journal this request in case of crash.  The put()
+                    // operation returned null when this package was not already
+                    // in the set; we want to avoid touching the disk redundantly.
+                    writeToJournalLocked(packageName);
+                }
+            }
+        }
+
+        // ...and schedule a backup pass if necessary
+        KeyValueBackupJob.schedule(mContext, mConstants);
+    }
+
+    // Note: packageName is currently unused, but may be in the future
+    private HashSet<String> dataChangedTargets(String packageName) {
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // backup of its own data.
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            synchronized (mBackupParticipants) {
+                return mBackupParticipants.get(Binder.getCallingUid());
+            }
+        }
+
+        // a caller with full permission can ask to back up any participating app
+        if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
+            return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
+        } else {
+            synchronized (mBackupParticipants) {
+                return SparseArrayUtils.union(mBackupParticipants);
+            }
+        }
+    }
+
+    private void writeToJournalLocked(String str) {
+        try {
+            if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
+            mJournal.addPackage(str);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't write " + str + " to backup journal", e);
+            mJournal = null;
+        }
+    }
+
+    // ----- IBackupManager binder interface -----
+
+    /** Sent from an app's backup agent to let the service know that there's new data to backup. */
+    public void dataChanged(final String packageName) {
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            // TODO: http://b/22388012
+            // App is running under a non-owner user profile.  For now, we do not back
+            // up data from secondary user profiles.
+            // TODO: backups for all user profiles although don't add backup for profiles
+            // without adding admin control in DevicePolicyManager.
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "dataChanged(" + packageName + ") ignored because it's user "
+                        + callingUserHandle);
+            }
+            return;
+        }
+
+        final HashSet<String> targets = dataChangedTargets(packageName);
+        if (targets == null) {
+            Slog.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+                    + " uid=" + Binder.getCallingUid());
+            return;
+        }
+
+        mBackupHandler.post(new Runnable() {
+            public void run() {
+                dataChangedImpl(packageName, targets);
+            }
+        });
+    }
+
+    /** Run an initialize operation for the given transport. */
+    public void initializeTransports(String[] transportNames, IBackupObserver observer) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "initializeTransport");
+        Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            mWakelock.acquire();
+            OnTaskFinishedListener listener = caller -> mWakelock.release();
+            mBackupHandler.post(
+                    new PerformInitializeTask(this, transportNames, observer, listener));
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Clear the given package's backup data from the current transport. */
+    public void clearBackupData(String transportName, String packageName) {
+        if (DEBUG) Slog.v(TAG, "clearBackupData() of " + packageName + " on " + transportName);
+        PackageInfo info;
+        try {
+            info = mPackageManager.getPackageInfo(packageName,
+                    PackageManager.GET_SIGNING_CERTIFICATES);
+        } catch (NameNotFoundException e) {
+            Slog.d(TAG, "No such package '" + packageName + "' - not clearing backup data");
+            return;
+        }
+
+        // If the caller does not hold the BACKUP permission, it can only request a
+        // wipe of its own backed-up data.
+        Set<String> apps;
+        if ((mContext.checkPermission(android.Manifest.permission.BACKUP, Binder.getCallingPid(),
+                Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) {
+            apps = mBackupParticipants.get(Binder.getCallingUid());
+        } else {
+            // a caller with full permission can ask to back up any participating app
+            // !!! TODO: allow data-clear of ANY app?
+            if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
+            apps = mProcessedPackagesJournal.getPackagesCopy();
+        }
+
+        if (apps.contains(packageName)) {
+            // found it; fire off the clear request
+            if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
+            mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
+            synchronized (mQueueLock) {
+                TransportClient transportClient =
+                        mTransportManager
+                                .getTransportClient(transportName, "BMS.clearBackupData()");
+                if (transportClient == null) {
+                    // transport is currently unregistered -- make sure to retry
+                    Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
+                            new ClearRetryParams(transportName, packageName));
+                    mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
+                    return;
+                }
+                long oldId = Binder.clearCallingIdentity();
+                OnTaskFinishedListener listener =
+                        caller ->
+                                mTransportManager.disposeOfTransportClient(transportClient, caller);
+                mWakelock.acquire();
+                Message msg = mBackupHandler.obtainMessage(
+                        MSG_RUN_CLEAR,
+                        new ClearParams(transportClient, info, listener));
+                mBackupHandler.sendMessage(msg);
+                Binder.restoreCallingIdentity(oldId);
+            }
+        }
+    }
+
+    /**
+     * Run a backup pass immediately for any applications that have declared that they have pending
+     * updates.
+     */
+    public void backupNow() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            final PowerSaveState result =
+                    mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
+            if (result.batterySaverEnabled) {
+                if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
+                KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
+            } else {
+                if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
+                synchronized (mQueueLock) {
+                    // Fire the intent that kicks off the whole shebang...
+                    try {
+                        mRunBackupIntent.send();
+                    } catch (PendingIntent.CanceledException e) {
+                        // should never happen
+                        Slog.e(TAG, "run-backup intent cancelled!");
+                    }
+
+                    // ...and cancel any pending scheduled job, because we've just superseded it
+                    KeyValueBackupJob.cancel(mContext);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Returns {@code true} if the system user has gone through SUW. */
+    public boolean deviceIsProvisioned() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages supplied via the command line, writing
+     * the resulting data stream to the supplied {@code fd}. This method is synchronous and does not
+     * return to the caller until the backup has been completed. It requires on-screen confirmation
+     * by the user.
+     */
+    public void adbBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
+            boolean includeShared, boolean doWidgets, boolean doAllApps, boolean includeSystem,
+            boolean compress, boolean doKeyValue, String[] pkgList) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbBackup");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Backup supported only for the device owner");
+        }
+
+        // Validate
+        if (!doAllApps) {
+            if (!includeShared) {
+                // If we're backing up shared data (sdcard or equivalent), then we can run
+                // without any supplied app names.  Otherwise, we'd be doing no work, so
+                // report the error.
+                if (pkgList == null || pkgList.length == 0) {
+                    throw new IllegalArgumentException(
+                            "Backup requested but neither shared nor any apps named");
+                }
+            }
+        }
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            // Doesn't make sense to do a full backup prior to setup
+            if (!deviceIsProvisioned()) {
+                Slog.i(TAG, "Backup not supported before setup");
+                return;
+            }
+
+            if (DEBUG) {
+                Slog.v(TAG, "Requesting backup: apks=" + includeApks + " obb=" + includeObbs
+                        + " shared=" + includeShared + " all=" + doAllApps + " system="
+                        + includeSystem + " includekeyvalue=" + doKeyValue + " pkgs=" + pkgList);
+            }
+            Slog.i(TAG, "Beginning adb backup...");
+
+            AdbBackupParams params = new AdbBackupParams(fd, includeApks, includeObbs,
+                    includeShared, doWidgets, doAllApps, includeSystem, compress, doKeyValue,
+                    pkgList);
+            final int token = generateRandomIntegerToken();
+            synchronized (mAdbBackupRestoreConfirmations) {
+                mAdbBackupRestoreConfirmations.put(token, params);
+            }
+
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting backup confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch backup confirmation UI");
+                mAdbBackupRestoreConfirmations.delete(token);
+                return;
+            }
+
+            // make sure the screen is lit for the user interaction
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
+
+            // start the confirmation countdown
+            startConfirmationTimeout(token, params);
+
+            // wait for the backup to be performed
+            if (DEBUG) Slog.d(TAG, "Waiting for backup completion...");
+            waitForCompletion(params);
+        } finally {
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Slog.e(TAG, "IO error closing output for adb backup: " + e.getMessage());
+            }
+            Binder.restoreCallingIdentity(oldId);
+            Slog.d(TAG, "Adb backup processing complete.");
+        }
+    }
+
+    /** Run a full backup pass for the given packages. Used by 'adb shell bmgr'. */
+    public void fullTransportBackup(String[] pkgNames) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "fullTransportBackup");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Restore supported only for the device owner");
+        }
+
+        String transportName = mTransportManager.getCurrentTransportName();
+        if (!fullBackupAllowable(transportName)) {
+            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "fullTransportBackup()");
+            }
+
+            final long oldId = Binder.clearCallingIdentity();
+            try {
+                CountDownLatch latch = new CountDownLatch(1);
+                Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
+                        this,
+                        /* observer */ null,
+                        pkgNames,
+                        /* updateSchedule */ false,
+                        /* runningJob */ null,
+                        latch,
+                        /* backupObserver */ null,
+                        /* monitor */ null,
+                        /* userInitiated */ false,
+                        "BMS.fullTransportBackup()");
+                // Acquiring wakelock for PerformFullTransportBackupTask before its start.
+                mWakelock.acquire();
+                (new Thread(task, "full-transport-master")).start();
+                do {
+                    try {
+                        latch.await();
+                        break;
+                    } catch (InterruptedException e) {
+                        // Just go back to waiting for the latch to indicate completion
+                    }
+                } while (true);
+
+                // We just ran a backup on these packages, so kick them to the end of the queue
+                final long now = System.currentTimeMillis();
+                for (String pkg : pkgNames) {
+                    enqueueFullBackup(pkg, now);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(oldId);
+            }
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "Done with full transport backup.");
+        }
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass, blocking until completion. Requires user
+     * confirmation.
+     */
+    public void adbRestore(ParcelFileDescriptor fd) {
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "adbRestore");
+
+        final int callingUserHandle = UserHandle.getCallingUserId();
+        // TODO: http://b/22388012
+        if (callingUserHandle != UserHandle.USER_SYSTEM) {
+            throw new IllegalStateException("Restore supported only for the device owner");
+        }
+
+        long oldId = Binder.clearCallingIdentity();
+
+        try {
+            // Check whether the device has been provisioned -- we don't handle
+            // full restores prior to completing the setup process.
+            if (!deviceIsProvisioned()) {
+                Slog.i(TAG, "Full restore not permitted before setup");
+                return;
+            }
+
+            Slog.i(TAG, "Beginning restore...");
+
+            AdbRestoreParams params = new AdbRestoreParams(fd);
+            final int token = generateRandomIntegerToken();
+            synchronized (mAdbBackupRestoreConfirmations) {
+                mAdbBackupRestoreConfirmations.put(token, params);
+            }
+
+            // start up the confirmation UI
+            if (DEBUG) Slog.d(TAG, "Starting restore confirmation UI, token=" + token);
+            if (!startConfirmationUi(token, FullBackup.FULL_RESTORE_INTENT_ACTION)) {
+                Slog.e(TAG, "Unable to launch restore confirmation");
+                mAdbBackupRestoreConfirmations.delete(token);
+                return;
+            }
+
+            // make sure the screen is lit for the user interaction
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
+
+            // start the confirmation countdown
+            startConfirmationTimeout(token, params);
+
+            // wait for the restore to be performed
+            if (DEBUG) Slog.d(TAG, "Waiting for restore completion...");
+            waitForCompletion(params);
+        } finally {
+            try {
+                fd.close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Error trying to close fd after adb restore: " + e);
+            }
+            Binder.restoreCallingIdentity(oldId);
+            Slog.i(TAG, "adb restore processing complete.");
+        }
+    }
+
+    private boolean startConfirmationUi(int token, String action) {
+        try {
+            Intent confIntent = new Intent(action);
+            confIntent.setClassName("com.android.backupconfirm",
+                    "com.android.backupconfirm.BackupRestoreConfirmation");
+            confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+            confIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            mContext.startActivityAsUser(confIntent, UserHandle.SYSTEM);
+        } catch (ActivityNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+    private void startConfirmationTimeout(int token, AdbParams params) {
+        if (MORE_DEBUG) {
+            Slog.d(TAG, "Posting conf timeout msg after "
+                    + TIMEOUT_FULL_CONFIRMATION + " millis");
+        }
+        Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+                token, 0, params);
+        mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+    }
+
+    private void waitForCompletion(AdbParams params) {
+        synchronized (params.latch) {
+            while (!params.latch.get()) {
+                try {
+                    params.latch.wait();
+                } catch (InterruptedException e) { /* never interrupted */ }
+            }
+        }
+    }
+
+    /** Called when adb backup/restore has completed. */
+    public void signalAdbBackupRestoreCompletion(AdbParams params) {
+        synchronized (params.latch) {
+            params.latch.set(true);
+            params.latch.notifyAll();
+        }
+    }
+
+    /**
+     * Confirm that the previously-requested full backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(int token, boolean allow,
+            String curPassword, String encPpassword, IFullBackupRestoreObserver observer) {
+        if (DEBUG) {
+            Slog.d(TAG, "acknowledgeAdbBackupOrRestore : token=" + token
+                    + " allow=" + allow);
+        }
+
+        // TODO: possibly require not just this signature-only permission, but even
+        // require that the specific designated confirmation-UI app uid is the caller?
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "acknowledgeAdbBackupOrRestore");
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+
+            AdbParams params;
+            synchronized (mAdbBackupRestoreConfirmations) {
+                params = mAdbBackupRestoreConfirmations.get(token);
+                if (params != null) {
+                    mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+                    mAdbBackupRestoreConfirmations.delete(token);
+
+                    if (allow) {
+                        final int verb = params instanceof AdbBackupParams
+                                ? MSG_RUN_ADB_BACKUP
+                                : MSG_RUN_ADB_RESTORE;
+
+                        params.observer = observer;
+                        params.curPassword = curPassword;
+
+                        params.encryptPassword = encPpassword;
+
+                        if (MORE_DEBUG) Slog.d(TAG, "Sending conf message with verb " + verb);
+                        mWakelock.acquire();
+                        Message msg = mBackupHandler.obtainMessage(verb, params);
+                        mBackupHandler.sendMessage(msg);
+                    } else {
+                        Slog.w(TAG, "User rejected full backup/restore operation");
+                        // indicate completion without having actually transferred any data
+                        signalAdbBackupRestoreCompletion(params);
+                    }
+                } else {
+                    Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** User-configurable enabling/disabling of backups. */
+    public void setBackupEnabled(boolean enable) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupEnabled");
+
+        Slog.i(TAG, "Backup enabled => " + enable);
+
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            boolean wasEnabled = mEnabled;
+            synchronized (this) {
+                // TODO(b/118520567): Clean up writing backup enabled logic.
+                BackupManagerService.writeBackupEnableState(enable, UserHandle.USER_SYSTEM);
+                mEnabled = enable;
+            }
+
+            synchronized (mQueueLock) {
+                if (enable && !wasEnabled && mProvisioned) {
+                    // if we've just been enabled, start scheduling backup passes
+                    KeyValueBackupJob.schedule(mContext, mConstants);
+                    scheduleNextFullBackupJob(0);
+                } else if (!enable) {
+                    // No longer enabled, so stop running backups
+                    if (MORE_DEBUG) Slog.i(TAG, "Opting out of backup");
+
+                    KeyValueBackupJob.cancel(mContext);
+
+                    // This also constitutes an opt-out, so we wipe any data for
+                    // this device from the backend.  We start that process with
+                    // an alarm in order to guarantee wakelock states.
+                    if (wasEnabled && mProvisioned) {
+                        // NOTE: we currently flush every registered transport, not just
+                        // the currently-active one.
+                        List<String> transportNames = new ArrayList<>();
+                        List<String> transportDirNames = new ArrayList<>();
+                        mTransportManager.forEachRegisteredTransport(
+                                name -> {
+                                    final String dirName;
+                                    try {
+                                        dirName =
+                                                mTransportManager
+                                                        .getTransportDirName(name);
+                                    } catch (TransportNotRegisteredException e) {
+                                        // Should never happen
+                                        Slog.e(TAG, "Unexpected unregistered transport", e);
+                                        return;
+                                    }
+                                    transportNames.add(name);
+                                    transportDirNames.add(dirName);
+                                });
+
+                        // build the set of transports for which we are posting an init
+                        for (int i = 0; i < transportNames.size(); i++) {
+                            recordInitPending(
+                                    true,
+                                    transportNames.get(i),
+                                    transportDirNames.get(i));
+                        }
+                        mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
+                                mRunInitIntent);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(boolean doAutoRestore) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setAutoRestore");
+
+        Slog.i(TAG, "Auto restore => " + doAutoRestore);
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                Settings.Secure.putInt(mContext.getContentResolver(),
+                        Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+                mAutoRestore = doAutoRestore;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Mark the backup service as having been provisioned. */
+    public void setBackupProvisioned(boolean available) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupProvisioned");
+        /*
+         * This is now a no-op; provisioning is simply the device's own setup state.
+         */
+    }
+
+    /** Report whether the backup mechanism is currently enabled. */
+    public boolean isBackupEnabled() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "isBackupEnabled");
+        return mEnabled;    // no need to synchronize just to read it
+    }
+
+    /** Report the name of the currently active transport. */
+    public String getCurrentTransport() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getCurrentTransport");
+        String currentTransport = mTransportManager.getCurrentTransportName();
+        if (MORE_DEBUG) Slog.v(TAG, "... getCurrentTransport() returning " + currentTransport);
+        return currentTransport;
+    }
+
+    /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+     * null} if no transport selected or if the transport selected is not registered.
+     */
+    @Nullable
+    public ComponentName getCurrentTransportComponent() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "getCurrentTransportComponent");
+        long oldId = Binder.clearCallingIdentity();
+        try {
+            return mTransportManager.getCurrentTransportComponent();
+        } catch (TransportNotRegisteredException e) {
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /** Report all known, available backup transports by name. */
+    public String[] listAllTransports() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "listAllTransports");
+
+        return mTransportManager.getRegisteredTransportNames();
+    }
+
+    /** Report all known, available backup transports by component. */
+    public ComponentName[] listAllTransportComponents() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "listAllTransportComponents");
+        return mTransportManager.getRegisteredTransportComponents();
+    }
+
+    /** Report all system whitelisted transports. */
+    public String[] getTransportWhitelist() {
+        // No permission check, intentionally.
+        Set<ComponentName> whitelistedComponents = mTransportManager.getTransportWhitelist();
+        String[] whitelistedTransports = new String[whitelistedComponents.size()];
+        int i = 0;
+        for (ComponentName component : whitelistedComponents) {
+            whitelistedTransports[i] = component.flattenToShortString();
+            i++;
+        }
+        return whitelistedTransports;
+    }
+
+    /**
+     * Update the attributes of the transport identified by {@code transportComponent}. If the
+     * specified transport has not been bound at least once (for registration), this call will be
+     * ignored. Only the host process of the transport can change its description, otherwise a
+     * {@link SecurityException} will be thrown.
+     *
+     * @param transportComponent The identity of the transport being described.
+     * @param name A {@link String} with the new name for the transport. This is NOT for
+     *     identification. MUST NOT be {@code null}.
+     * @param configurationIntent An {@link Intent} that can be passed to
+     *     {@link Context#startActivity} in order to launch the transport's configuration UI. It may
+     *     be {@code null} if the transport does not offer any user-facing configuration UI.
+     * @param currentDestinationString A {@link String} describing the destination to which the
+     *     transport is currently sending data. MUST NOT be {@code null}.
+     * @param dataManagementIntent An {@link Intent} that can be passed to
+     *     {@link Context#startActivity} in order to launch the transport's data-management UI. It
+     *     may be {@code null} if the transport does not offer any user-facing data
+     *     management UI.
+     * @param dataManagementLabel A {@link String} to be used as the label for the transport's data
+     *     management affordance. This MUST be {@code null} when dataManagementIntent is
+     *     {@code null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+     * @throws SecurityException If the UID of the calling process differs from the package UID of
+     *     {@code transportComponent} or if the caller does NOT have BACKUP permission.
+     */
+    public void updateTransportAttributes(
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            @Nullable String dataManagementLabel) {
+        updateTransportAttributes(
+                Binder.getCallingUid(),
+                transportComponent,
+                name,
+                configurationIntent,
+                currentDestinationString,
+                dataManagementIntent,
+                dataManagementLabel);
+    }
+
+    @VisibleForTesting
+    void updateTransportAttributes(
+            int callingUid,
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            @Nullable String dataManagementLabel) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "updateTransportAttributes");
+
+        Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
+        Preconditions.checkNotNull(name, "name can't be null");
+        Preconditions.checkNotNull(
+                currentDestinationString, "currentDestinationString can't be null");
+        Preconditions.checkArgument(
+                (dataManagementIntent == null) == (dataManagementLabel == null),
+                "dataManagementLabel should be null iff dataManagementIntent is null");
+
+        try {
+            int transportUid =
+                    mContext.getPackageManager()
+                            .getPackageUid(transportComponent.getPackageName(), 0);
+            if (callingUid != transportUid) {
+                throw new SecurityException("Only the transport can change its description");
+            }
+        } catch (NameNotFoundException e) {
+            throw new SecurityException("Transport package not found", e);
+        }
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            mTransportManager.updateTransportAttributes(
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /**
+     * Selects transport {@code transportName} and returns previously selected transport.
+     *
+     * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} instead.
+     */
+    @Deprecated
+    @Nullable
+    public String selectBackupTransport(String transportName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransport");
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            String previousTransportName = mTransportManager.selectTransport(transportName);
+            updateStateForTransport(transportName);
+            Slog.v(TAG, "selectBackupTransport(transport = " + transportName
+                    + "): previous transport = " + previousTransportName);
+            return previousTransportName;
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    /**
+     * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+     * with the result upon completion.
+     */
+    public void selectBackupTransportAsync(
+            ComponentName transportComponent, ISelectBackupTransportCallback listener) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "selectBackupTransportAsync");
+
+        final long oldId = Binder.clearCallingIdentity();
+        try {
+            String transportString = transportComponent.flattenToShortString();
+            Slog.v(TAG, "selectBackupTransportAsync(transport = " + transportString + ")");
+            mBackupHandler.post(
+                    () -> {
+                        String transportName = null;
+                        int result =
+                                mTransportManager.registerAndSelectTransport(transportComponent);
+                        if (result == BackupManager.SUCCESS) {
+                            try {
+                                transportName =
+                                        mTransportManager.getTransportName(transportComponent);
+                                updateStateForTransport(transportName);
+                            } catch (TransportNotRegisteredException e) {
+                                Slog.e(TAG, "Transport got unregistered");
+                                result = BackupManager.ERROR_TRANSPORT_UNAVAILABLE;
+                            }
+                        }
+
+                        try {
+                            if (transportName != null) {
+                                listener.onSuccess(transportName);
+                            } else {
+                                listener.onFailure(result);
+                            }
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "ISelectBackupTransportCallback listener not available");
+                        }
+                    });
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
+    }
+
+    private void updateStateForTransport(String newTransportName) {
+        // Publish the name change
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+
+        // And update our current-dataset bookkeeping
+        String callerLogString = "BMS.updateStateForTransport()";
+        TransportClient transportClient =
+                mTransportManager.getTransportClient(newTransportName, callerLogString);
+        if (transportClient != null) {
+            try {
+                IBackupTransport transport = transportClient.connectOrThrow(callerLogString);
+                mCurrentToken = transport.getCurrentRestoreSet();
+            } catch (Exception e) {
+                // Oops.  We can't know the current dataset token, so reset and figure it out
+                // when we do the next k/v backup operation on this transport.
+                mCurrentToken = 0;
+                Slog.w(TAG, "Transport " + newTransportName + " not available: current token = 0");
+            }
+            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+        } else {
+            Slog.w(TAG, "Transport " + newTransportName + " not registered: current token = 0");
+            // The named transport isn't registered, so we can't know what its current dataset token
+            // is. Reset as above.
+            mCurrentToken = 0;
+        }
+    }
+
+    /**
+     * Supply the configuration intent for the given transport. If the name is not one of the
+     * available transports, or if the transport does not supply any configuration UI, the method
+     * returns {@code null}.
+     */
+    public Intent getConfigurationIntent(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getConfigurationIntent");
+        try {
+            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
+            }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Supply the current destination string for the given transport. If the name is not one of the
+     * registered transports the method will return null.
+     *
+     * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+     *
+     * @param transportName The name of the registered transport.
+     * @return The current destination string or null if the transport is not registered.
+     */
+    public String getDestinationString(String transportName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "getDestinationString");
+
+        try {
+            String string = mTransportManager.getTransportCurrentDestinationString(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDestinationString() returning " + string);
+            }
+            return string;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get destination string from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /** Supply the manage-data intent for the given transport. */
+    public Intent getDataManagementIntent(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getDataManagementIntent");
+
+        try {
+            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
+            }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Supply the menu label for affordances that fire the manage-data intent for the given
+     * transport.
+     */
+    public String getDataManagementLabel(String transportName) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "getDataManagementLabel");
+
+        try {
+            String label = mTransportManager.getTransportDataManagementLabel(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementLabel() returning " + label);
+            }
+            return label;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
+     */
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        synchronized (mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+                IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
+                mConnectedAgent = agent;
+                mConnecting = false;
+            } else {
+                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent connected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    /**
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. If the agent failed
+     * to come up in the first place, the agentBinder argument will be {@code null}. This should
+     * only be called from the {@link ActivityManager}.
+     */
+    public void agentDisconnected(String packageName) {
+        // TODO: handle backup being interrupted
+        synchronized (mAgentConnectLock) {
+            if (Binder.getCallingUid() == Process.SYSTEM_UID) {
+                mConnectedAgent = null;
+                mConnecting = false;
+            } else {
+                Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                        + " claiming agent disconnected");
+            }
+            mAgentConnectLock.notifyAll();
+        }
+    }
+
+    /**
+     * An application being installed will need a restore pass, then the {@link PackageManager} will
+     * need to be told when the restore is finished.
+     */
+    public void restoreAtInstall(String packageName, int token) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+                    + " attemping install-time restore");
+            return;
+        }
+
+        boolean skip = false;
+
+        long restoreSet = getAvailableRestoreToken(packageName);
+        if (DEBUG) {
+            Slog.v(TAG, "restoreAtInstall pkg=" + packageName
+                    + " token=" + Integer.toHexString(token)
+                    + " restoreSet=" + Long.toHexString(restoreSet));
+        }
+        if (restoreSet == 0) {
+            if (MORE_DEBUG) Slog.i(TAG, "No restore set");
+            skip = true;
+        }
+
+        TransportClient transportClient =
+                mTransportManager.getCurrentTransportClient("BMS.restoreAtInstall()");
+        if (transportClient == null) {
+            if (DEBUG) Slog.w(TAG, "No transport client");
+            skip = true;
+        }
+
+        if (!mAutoRestore) {
+            if (DEBUG) {
+                Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore);
+            }
+            skip = true;
+        }
+
+        if (!skip) {
+            try {
+                // okay, we're going to attempt a restore of this package from this restore set.
+                // The eventual message back into the Package Manager to run the post-install
+                // steps for 'token' will be issued from the restore handling code.
+
+                mWakelock.acquire();
+
+                OnTaskFinishedListener listener = caller -> {
+                    mTransportManager.disposeOfTransportClient(transportClient, caller);
+                    mWakelock.release();
+                };
+
+                if (MORE_DEBUG) {
+                    Slog.d(TAG, "Restore at install of " + packageName);
+                }
+                Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                msg.obj =
+                        RestoreParams.createForRestoreAtInstall(
+                                transportClient,
+                                /* observer */ null,
+                                /* monitor */ null,
+                                restoreSet,
+                                packageName,
+                                token,
+                                listener);
+                mBackupHandler.sendMessage(msg);
+            } catch (Exception e) {
+                // Calling into the transport broke; back off and proceed with the installation.
+                Slog.e(TAG, "Unable to contact transport: " + e.getMessage());
+                skip = true;
+            }
+        }
+
+        if (skip) {
+            // Auto-restore disabled or no way to attempt a restore
+
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(
+                        transportClient, "BMS.restoreAtInstall()");
+            }
+
+            // Tell the PackageManager to proceed with the post-install handling for this package.
+            if (DEBUG) Slog.v(TAG, "Finishing install immediately");
+            try {
+                mPackageManagerBinder.finishPackageInstall(token, false);
+            } catch (RemoteException e) { /* can't happen */ }
+        }
+    }
+
+    /** Hand off a restore session. */
+    public IRestoreSession beginRestoreSession(String packageName, String transport) {
+        if (DEBUG) {
+            Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+                    + " transport=" + transport);
+        }
+
+        boolean needPermission = true;
+        if (transport == null) {
+            transport = mTransportManager.getCurrentTransportName();
+
+            if (packageName != null) {
+                PackageInfo app = null;
+                try {
+                    app = mPackageManager.getPackageInfo(packageName, 0);
+                } catch (NameNotFoundException nnf) {
+                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+                    throw new IllegalArgumentException("Package " + packageName + " not found");
+                }
+
+                if (app.applicationInfo.uid == Binder.getCallingUid()) {
+                    // So: using the current active transport, and the caller has asked
+                    // that its own package will be restored.  In this narrow use case
+                    // we do not require the caller to hold the permission.
+                    needPermission = false;
+                }
+            }
+        }
+
+        if (needPermission) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                    "beginRestoreSession");
+        } else {
+            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+        }
+
+        synchronized (this) {
+            if (mActiveRestoreSession != null) {
+                Slog.i(TAG, "Restore session requested but one already active");
+                return null;
+            }
+            if (mBackupRunning) {
+                Slog.i(TAG, "Restore session requested but currently running backups");
+                return null;
+            }
+            mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
+            mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
+        }
+        return mActiveRestoreSession;
+    }
+
+    /** Clear the specified restore session. */
+    public void clearRestoreSession(ActiveRestoreSession currentSession) {
+        synchronized (this) {
+            if (currentSession != mActiveRestoreSession) {
+                Slog.e(TAG, "ending non-current restore session");
+            } else {
+                if (DEBUG) Slog.v(TAG, "Clearing restore session and halting timeout");
+                mActiveRestoreSession = null;
+                mBackupHandler.removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
+            }
+        }
+    }
+
+    /**
+     * Note that a currently-active backup agent has notified us that it has completed the given
+     * outstanding asynchronous backup/restore operation.
+     */
+    public void opComplete(int token, long result) {
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+        }
+        Operation op = null;
+        synchronized (mCurrentOpLock) {
+            op = mCurrentOperations.get(token);
+            if (op != null) {
+                if (op.state == OP_TIMEOUT) {
+                    // The operation already timed out, and this is a late response.  Tidy up
+                    // and ignore it; we've already dealt with the timeout.
+                    op = null;
+                    mCurrentOperations.delete(token);
+                } else if (op.state == OP_ACKNOWLEDGED) {
+                    if (DEBUG) {
+                        Slog.w(TAG, "Received duplicate ack for token="
+                                + Integer.toHexString(token));
+                    }
+                    op = null;
+                    mCurrentOperations.remove(token);
+                } else if (op.state == OP_PENDING) {
+                    // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
+                    // called after we we receive this call.
+                    op.state = OP_ACKNOWLEDGED;
+                }
+            }
+            mCurrentOpLock.notifyAll();
+        }
+
+        // The completion callback, if any, is invoked on the handler
+        if (op != null && op.callback != null) {
+            Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+            Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
+            mBackupHandler.sendMessage(msg);
+        }
+    }
+
+    /** Checks if the package is eligible for backup. */
+    public boolean isAppEligibleForBackup(String packageName) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
+
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.isAppEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            boolean eligible =
+                    AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+                            transportClient, packageName, mPackageManager);
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligible;
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Returns the inputted packages that are eligible for backup. */
+    public String[] filterAppsEligibleForBackup(String[] packages) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
+
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.filterAppsEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            List<String> eligibleApps = new LinkedList<>();
+            for (String packageName : packages) {
+                if (AppBackupUtils
+                        .appIsRunningAndEligibleForBackupWithTransport(
+                                transportClient, packageName, mPackageManager)) {
+                    eligibleApps.add(packageName);
+                }
+            }
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligibleApps.toArray(new String[eligibleApps.size()]);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
+        }
+    }
+
+    /** Prints service state for 'dumpsys backup'. */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+
+        long identityToken = Binder.clearCallingIdentity();
+        try {
+            if (args != null) {
+                for (String arg : args) {
+                    if ("-h".equals(arg)) {
+                        pw.println("'dumpsys backup' optional arguments:");
+                        pw.println("  -h       : this help text");
+                        pw.println("  a[gents] : dump information about defined backup agents");
+                        return;
+                    } else if ("agents".startsWith(arg)) {
+                        dumpAgents(pw);
+                        return;
+                    } else if ("transportclients".equals(arg.toLowerCase())) {
+                        mTransportManager.dumpTransportClients(pw);
+                        return;
+                    } else if ("transportstats".equals(arg.toLowerCase())) {
+                        mTransportManager.dumpTransportStats(pw);
+                        return;
+                    }
+                }
+            }
+            dumpInternal(pw);
+        } finally {
+            Binder.restoreCallingIdentity(identityToken);
+        }
+    }
+
+    private void dumpAgents(PrintWriter pw) {
+        List<PackageInfo> agentPackages = allAgentPackages();
+        pw.println("Defined backup agents:");
+        for (PackageInfo pkg : agentPackages) {
+            pw.print("  ");
+            pw.print(pkg.packageName);
+            pw.println(':');
+            pw.print("      ");
+            pw.println(pkg.applicationInfo.backupAgentName);
+        }
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        synchronized (mQueueLock) {
+            pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+                    + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
+                    + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
+            pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
+            if (mBackupRunning) pw.println("Backup currently running");
+            pw.println(isBackupOperationInProgress() ? "Backup in progress" : "No backups running");
+            pw.println("Last backup pass started: " + mLastBackupPass
+                    + " (now = " + System.currentTimeMillis() + ')');
+            pw.println("  next scheduled: " + KeyValueBackupJob.nextScheduled());
+
+            pw.println("Transport whitelist:");
+            for (ComponentName transport : mTransportManager.getTransportWhitelist()) {
+                pw.print("    ");
+                pw.println(transport.flattenToShortString());
+            }
+
+            pw.println("Available transports:");
+            final String[] transports = listAllTransports();
+            if (transports != null) {
+                for (String t : transports) {
+                    pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
+                            : "    ") + t);
+                    try {
+                        File dir = new File(mBaseStateDir,
+                                mTransportManager.getTransportDirName(t));
+                        pw.println("       destination: "
+                                + mTransportManager.getTransportCurrentDestinationString(t));
+                        pw.println("       intent: "
+                                + mTransportManager.getTransportConfigurationIntent(t));
+                        for (File f : dir.listFiles()) {
+                            pw.println(
+                                    "       " + f.getName() + " - " + f.length() + " state bytes");
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Error in transport", e);
+                        pw.println("        Error: " + e);
+                    }
+                }
+            }
+
+            mTransportManager.dumpTransportClients(pw);
+
+            pw.println("Pending init: " + mPendingInits.size());
+            for (String s : mPendingInits) {
+                pw.println("    " + s);
+            }
+
+            pw.print("Ancestral: ");
+            pw.println(Long.toHexString(mAncestralToken));
+            pw.print("Current:   ");
+            pw.println(Long.toHexString(mCurrentToken));
+
+            int numPackages = mBackupParticipants.size();
+            pw.println("Participants:");
+            for (int i = 0; i < numPackages; i++) {
+                int uid = mBackupParticipants.keyAt(i);
+                pw.print("  uid: ");
+                pw.println(uid);
+                HashSet<String> participants = mBackupParticipants.valueAt(i);
+                for (String app : participants) {
+                    pw.println("    " + app);
+                }
+            }
+
+            pw.println("Ancestral packages: "
+                    + (mAncestralPackages == null ? "none" : mAncestralPackages.size()));
+            if (mAncestralPackages != null) {
+                for (String pkg : mAncestralPackages) {
+                    pw.println("    " + pkg);
+                }
+            }
+
+            Set<String> processedPackages = mProcessedPackagesJournal.getPackagesCopy();
+            pw.println("Ever backed up: " + processedPackages.size());
+            for (String pkg : processedPackages) {
+                pw.println("    " + pkg);
+            }
+
+            pw.println("Pending key/value backup: " + mPendingBackups.size());
+            for (BackupRequest req : mPendingBackups.values()) {
+                pw.println("    " + req);
+            }
+
+            pw.println("Full backup queue:" + mFullBackupQueue.size());
+            for (FullBackupEntry entry : mFullBackupQueue) {
+                pw.print("    ");
+                pw.print(entry.lastBackup);
+                pw.print(" : ");
+                pw.println(entry.packageName);
+            }
+        }
+    }
+
+
+    public IBackupManager getBackupManagerBinder() {
+        return mBackupManagerBinder;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index 94365d7..bace1aa 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -1,10 +1,10 @@
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 
 import android.annotation.Nullable;
 import android.app.backup.FullBackup;
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index c9f7218..5e92339 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -39,8 +39,8 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteCall;
 import com.android.server.backup.utils.FullBackupUtils;
 
@@ -53,7 +53,7 @@
  * and emitting it to the designated OutputStream.
  */
 public class FullBackupEngine {
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     OutputStream mOutput;
     FullBackupPreflight mPreflightHook;
     BackupRestoreTask mTimeoutMonitor;
@@ -164,24 +164,21 @@
         }
 
         /**
-         * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+         * Don't write apks for system-bundled apps that are not upgraded.
          */
         private boolean shouldWriteApk(
                 ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
-            boolean isForwardLocked =
-                    (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
             boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
             boolean isUpdatedSystemApp =
                     (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
             return includeApks
                     && !isSharedStorage
-                    && !isForwardLocked
                     && (!isSystemApp || isUpdatedSystemApp);
         }
     }
 
     public FullBackupEngine(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             OutputStream output,
             FullBackupPreflight preflightHook,
             PackageInfo pkg,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index bc7d9fc..e142537 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.fullbackup;
 
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -34,7 +34,7 @@
 import com.android.internal.backup.IObbBackupService;
 import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.IOException;
@@ -45,11 +45,11 @@
  */
 public class FullBackupObbConnection implements ServiceConnection {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     volatile IObbBackupService mService;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public FullBackupObbConnection(BackupManagerService backupManagerService) {
+    public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
         mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 44edabc..43a80c4 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.backup.IFullBackupRestoreObserver;
 import android.content.pm.ApplicationInfo;
@@ -37,7 +37,7 @@
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.KeyValueAdbBackupEngine;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.PasswordUtils;
 
@@ -66,7 +66,7 @@
  */
 public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     FullBackupEngine mBackupEngine;
     final AtomicBoolean mLatch;
 
@@ -86,7 +86,7 @@
     String mEncryptPassword;
     private final int mCurrentOpToken;
 
-    public PerformAdbBackupTask(BackupManagerService backupManagerService,
+    public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
             ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
             boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
             String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 755095e..5b449c5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,9 @@
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
 
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
@@ -45,10 +45,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
 import com.android.server.backup.remote.RemoteCall;
@@ -97,7 +97,7 @@
  */
 public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
     public static PerformFullTransportBackupTask newWithCurrentTransport(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             IFullBackupRestoreObserver observer,
             String[] whichPackages,
             boolean updateSchedule,
@@ -128,7 +128,7 @@
 
     private static final String TAG = "PFTBT";
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     private final Object mCancelLock = new Object();
 
     ArrayList<PackageInfo> mPackages;
@@ -150,7 +150,7 @@
     private final int mCurrentOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
+    public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IFullBackupRestoreObserver observer,
             String[] whichPackages, boolean updateSchedule,
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index f66d8cc..ba153bf 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,10 +35,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.PerformAdbBackupTask;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.keyvalue.BackupRequest;
@@ -84,10 +84,10 @@
     public static final int MSG_BACKUP_RESTORE_STEP = 20;
     public static final int MSG_OP_COMPLETE = 21;
 
-    private final BackupManagerService backupManagerService;
+    private final UserBackupManagerService backupManagerService;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
+    public BackupHandler(UserBackupManagerService backupManagerService, Looper looper) {
         super(looper);
         this.backupManagerService = backupManagerService;
         mAgentTimeoutParameters = Preconditions.checkNotNull(
diff --git a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
index b0b8037..396f369 100644
--- a/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ClearDataObserver.java
@@ -18,13 +18,13 @@
 
 import android.content.pm.IPackageDataObserver;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class ClearDataObserver extends IPackageDataObserver.Stub {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
-    public ClearDataObserver(BackupManagerService backupManagerService) {
+    public ClearDataObserver(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index d028104..5ffa795 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -22,20 +22,20 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
 
 public class PerformClearTask implements Runnable {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final TransportManager mTransportManager;
     private final TransportClient mTransportClient;
     private final PackageInfo mPackage;
     private final OnTaskFinishedListener mListener;
 
-    PerformClearTask(BackupManagerService backupManagerService,
+    PerformClearTask(UserBackupManagerService backupManagerService,
             TransportClient transportClient, PackageInfo packageInfo,
             OnTaskFinishedListener listener) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 1ef740d..6b78fbf 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -30,8 +30,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
@@ -49,7 +49,7 @@
  * operation was successful then it's {@link BackupTransport#TRANSPORT_OK}.
  */
 public class PerformInitializeTask implements Runnable {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final TransportManager mTransportManager;
     private final String[] mQueue;
     private final File mBaseStateDir;
@@ -57,7 +57,7 @@
     @Nullable private IBackupObserver mObserver;
 
     public PerformInitializeTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             String[] transportNames,
             @Nullable IBackupObserver observer,
             OnTaskFinishedListener listener) {
@@ -72,7 +72,7 @@
 
     @VisibleForTesting
     PerformInitializeTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportManager transportManager,
             String[] transportNames,
             @Nullable IBackupObserver observer,
diff --git a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
index 69720d4..7e2ac79 100644
--- a/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/ProvisionedObserver.java
@@ -23,15 +23,15 @@
 import android.os.Handler;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.UserBackupManagerService;
 
 public class ProvisionedObserver extends ContentObserver {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
     public ProvisionedObserver(
-            BackupManagerService backupManagerService, Handler handler) {
+            UserBackupManagerService backupManagerService, Handler handler) {
         super(handler);
         this.backupManagerService = backupManagerService;
     }
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 6f574ca..2a5d913 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -18,8 +18,8 @@
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_BACKUP_ACTION;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
 
 import android.app.PendingIntent;
@@ -29,13 +29,13 @@
 import android.os.Message;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class RunBackupReceiver extends BroadcastReceiver {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
 
-    public RunBackupReceiver(BackupManagerService backupManagerService) {
+    public RunBackupReceiver(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 548c580..38870cb 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.internal;
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.RUN_INITIALIZE_ACTION;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.RUN_INITIALIZE_ACTION;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -27,12 +27,12 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 public class RunInitializeReceiver extends BroadcastReceiver {
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
-    public RunInitializeReceiver(BackupManagerService backupManagerService) {
+    public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
         mBackupManagerService = backupManagerService;
     }
 
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index bb8a1d1..535c7cb 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -30,6 +30,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.remote.RemoteResult;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
@@ -54,7 +55,7 @@
 public class KeyValueBackupReporter {
     @VisibleForTesting static final String TAG = "KeyValueBackupTask";
     private static final boolean DEBUG = BackupManagerService.DEBUG;
-    @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG || false;
+    @VisibleForTesting static final boolean MORE_DEBUG = BackupManagerService.MORE_DEBUG;
 
     static void onNewThread(String threadName) {
         if (DEBUG) {
@@ -62,12 +63,12 @@
         }
     }
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final IBackupObserver mObserver;
     @Nullable private IBackupManagerMonitor mMonitor;
 
     KeyValueBackupReporter(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             IBackupObserver observer,
             @Nullable IBackupManagerMonitor monitor) {
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index d6f2a87..f39d795 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -22,9 +22,9 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
 
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.BackupManagerService.OP_PENDING;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -35,7 +35,6 @@
 import android.app.backup.BackupManager;
 import android.app.backup.BackupTransport;
 import android.app.backup.IBackupCallback;
-import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.content.pm.ApplicationInfo;
@@ -55,11 +54,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.internal.Operation;
@@ -95,12 +94,12 @@
  * <p>A few definitions:
  *
  * <ul>
- *   <li>State directory: {@link BackupManagerService#getBaseStateDir()}/&lt;transport&gt;
+ *   <li>State directory: {@link UserBackupManagerService#getBaseStateDir()}/&lt;transport&gt;
  *   <li>State file: {@link
- *       BackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
+ *       UserBackupManagerService#getBaseStateDir()}/&lt;transport&gt;/&lt;package&gt;<br>
  *       Represents the state of the backup data for a specific package in the current dataset.
- *   <li>Stage directory: {@link BackupManagerService#getDataDir()}
- *   <li>Stage file: {@link BackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
+ *   <li>Stage directory: {@link UserBackupManagerService#getDataDir()}
+ *   <li>Stage file: {@link UserBackupManagerService#getDataDir()}/&lt;package&gt;.data<br>
  *       Contains staged data that the agents wrote via {@link BackupDataOutput}, to be transmitted
  *       to the transport.
  * </ul>
@@ -112,7 +111,7 @@
  * of incremental choice. If non-incremental, PM will only be backed-up if specified in the queue,
  * and if it's the case it will be re-positioned at the head of the queue.
  *
- * <p>Before starting, this task will register itself in {@link BackupManagerService} current
+ * <p>Before starting, this task will register itself in {@link UserBackupManagerService} current
  * operations.
  *
  * <p>In summary, this task will for each package:
@@ -121,7 +120,7 @@
  *   <li>Bind to its {@link IBackupAgent}.
  *   <li>Request transport quota and flags.
  *   <li>Call {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
- *       ParcelFileDescriptor, long, int, IBackupManager, int)} via {@link RemoteCall} passing the
+ *       ParcelFileDescriptor, long, IBackupCallback, int)} via {@link RemoteCall} passing the
  *       old state file descriptor (read), the backup data file descriptor (write), the new state
  *       file descriptor (write), the quota and the transport flags. This will call {@link
  *       BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} with
@@ -131,7 +130,7 @@
  *       <ul>
  *         <li>Agent response.
  *         <li>Agent time-out (specified via {@link
- *             BackupManagerService#getAgentTimeoutParameters()}.
+ *             UserBackupManagerService#getAgentTimeoutParameters()}.
  *         <li>External cancellation or thread interrupt.
  *       </ul>
  *   <li>Unbind the agent.
@@ -149,11 +148,11 @@
  *   <li>Mark data-changed for the remaining packages in the queue (skipped packages).
  *   <li>Delete the {@link DataChangedJournal} provided. Note that this should not be the current
  *       journal.
- *   <li>Set {@link BackupManagerService} current token as {@link
+ *   <li>Set {@link UserBackupManagerService} current token as {@link
  *       IBackupTransport#getCurrentRestoreSet()}, if applicable.
  *   <li>Add the transport to the list of transports pending initialization ({@link
- *       BackupManagerService#getPendingInits()}) and kick-off initialization if the transport ever
- *       returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
+ *       UserBackupManagerService#getPendingInits()}) and kick-off initialization if the transport
+ *       ever returned {@link BackupTransport#TRANSPORT_NOT_INITIALIZED}.
  *   <li>Unregister the task in current operations.
  *   <li>Release the wakelock.
  *   <li>Kick-off {@link PerformFullTransportBackupTask} if a list of full-backup packages was
@@ -174,7 +173,7 @@
     private static final int THREAD_PRIORITY = Process.THREAD_PRIORITY_BACKGROUND;
     private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
     private static final String BLANK_STATE_FILE_NAME = "blank_state";
-    private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
+    private static final String PM_PACKAGE = UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
     @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
     @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
 
@@ -182,7 +181,7 @@
      * Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
      * dedicated thread and kicks off the operation in it.
      *
-     * @param backupManagerService The {@link BackupManagerService} system service.
+     * @param backupManagerService The {@link UserBackupManagerService} instance.
      * @param transportClient The {@link TransportClient} that contains the transport used for the
      *     operation.
      * @param transportDirName The value of {@link IBackupTransport#transportDirName()} for the
@@ -201,7 +200,7 @@
      * @return The {@link KeyValueBackupTask} that was started.
      */
     public static KeyValueBackupTask start(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
@@ -232,7 +231,7 @@
         return task;
     }
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final PackageManager mPackageManager;
     private final TransportManager mTransportManager;
     private final TransportClient mTransportClient;
@@ -289,7 +288,7 @@
 
     @VisibleForTesting
     public KeyValueBackupTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
diff --git a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
index 28d85a6..bfc97ae 100644
--- a/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
+++ b/services/backup/java/com/android/server/backup/remote/ServiceBackupCallback.java
@@ -20,12 +20,12 @@
 import android.app.backup.IBackupManager;
 import android.os.RemoteException;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 /**
  * An implementation of {@link IBackupCallback} that routes the result to {@link
- * BackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token provided
- * in the constructor.
+ * UserBackupManagerService} via {@link IBackupManager#opComplete(int, long)} passing the token
+ * provided in the constructor.
  */
 public class ServiceBackupCallback extends IBackupCallback.Stub {
     private final IBackupManager mBackupManager;
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 140dded..e273b32 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -36,8 +36,8 @@
 import android.os.PowerManager;
 import android.util.Slog;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.params.RestoreGetSetsParams;
 import com.android.server.backup.params.RestoreParams;
@@ -53,14 +53,14 @@
 
     private final TransportManager mTransportManager;
     private final String mTransportName;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     @Nullable private final String mPackageName;
     public RestoreSet[] mRestoreSets = null;
     boolean mEnded = false;
     boolean mTimedOut = false;
 
     public ActiveRestoreSession(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             @Nullable String packageName,
             String transportName) {
         mBackupManagerService = backupManagerService;
@@ -405,10 +405,10 @@
     // Posted to the handler to tear down a restore session in a cleanly synchronized way
     public class EndRestoreRunnable implements Runnable {
 
-        BackupManagerService mBackupManager;
+        UserBackupManagerService mBackupManager;
         ActiveRestoreSession mSession;
 
-        public EndRestoreRunnable(BackupManagerService manager, ActiveRestoreSession session) {
+        public EndRestoreRunnable(UserBackupManagerService manager, ActiveRestoreSession session) {
             mBackupManager = manager;
             mSession = session;
         }
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index a8c7ce6..e4890e0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,8 +23,8 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.UserBackupManagerService;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -35,12 +35,12 @@
 public class AdbRestoreFinishedLatch implements BackupRestoreTask {
 
     private static final String TAG = "AdbRestoreFinishedLatch";
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     final CountDownLatch mLatch;
     private final int mCurrentOpToken;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
-    public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
+    public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
             int currentOpToken) {
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
index dc7044e..184a6d0 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedRunnable.java
@@ -3,7 +3,7 @@
 import android.app.IBackupAgent;
 import android.os.RemoteException;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 /**
  * Runner that can be placed on a separate thread to do in-process invocation of the "restore
@@ -13,10 +13,10 @@
 
     private final IBackupAgent mAgent;
     private final int mToken;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
     AdbRestoreFinishedRunnable(IBackupAgent agent, int token,
-            BackupManagerService backupManagerService) {
+            UserBackupManagerService backupManagerService) {
         mAgent = agent;
         mToken = token;
         mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 1084f52..0d26ea5 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -16,13 +16,13 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -44,10 +44,10 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -57,7 +57,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -68,7 +67,7 @@
  */
 public class FullRestoreEngine extends RestoreEngine {
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     // Task in charge of monitoring timeouts
     private final BackupRestoreTask mMonitorTask;
 
@@ -129,7 +128,7 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     final boolean mIsAdbRestore;
 
-    public FullRestoreEngine(BackupManagerService backupManagerService,
+    public FullRestoreEngine(UserBackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
             boolean allowObbs, int ephemeralOpToken, boolean isAdbRestore) {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 32dbad9..c904256 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,18 +16,15 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
-import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
 import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
 import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
-import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
-
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_FILE_VERSION;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
@@ -36,15 +33,12 @@
 import android.content.pm.Signature;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
-import com.android.server.backup.PackageManagerBackupAgent;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
 import com.android.server.backup.utils.PasswordUtils;
@@ -72,7 +66,7 @@
 
 public class PerformAdbRestoreTask implements Runnable {
 
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
     private final ParcelFileDescriptor mInputFile;
     private final String mCurrentPassword;
     private final String mDecryptPassword;
@@ -106,7 +100,7 @@
     // Packages we've already wiped data on when restoring their first file
     private final HashSet<String> mClearedPackages = new HashSet<>();
 
-    public PerformAdbRestoreTask(BackupManagerService backupManagerService,
+    public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
             ParcelFileDescriptor fd, String curPassword, String decryptPassword,
             IFullBackupRestoreObserver observer, AtomicBoolean latch) {
         this.mBackupManagerService = backupManagerService;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 580f70a..f7efad6 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -17,12 +17,12 @@
 package com.android.server.backup.restore;
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.KEY_WIDGET_STATE;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
+import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
+import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
+import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -40,8 +40,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -62,8 +62,8 @@
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.utils.AppBackupUtils;
@@ -80,7 +80,7 @@
 
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
 
-    private BackupManagerService backupManagerService;
+    private UserBackupManagerService backupManagerService;
     private final TransportManager mTransportManager;
     // Transport client we're working with to do the restore
     private final TransportClient mTransportClient;
@@ -164,7 +164,7 @@
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
     // about releasing it.
     public PerformUnifiedRestoreTask(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
diff --git a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
index 635b6d6..c4aa2d7 100644
--- a/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
+++ b/services/backup/java/com/android/server/backup/restore/RestoreFileRunnable.java
@@ -21,7 +21,7 @@
 import android.os.RemoteException;
 
 import com.android.server.backup.FileMetadata;
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 
 import java.io.IOException;
 
@@ -35,9 +35,9 @@
     private final FileMetadata mInfo;
     private final ParcelFileDescriptor mSocket;
     private final int mToken;
-    private final BackupManagerService mBackupManagerService;
+    private final UserBackupManagerService mBackupManagerService;
 
-    RestoreFileRunnable(BackupManagerService backupManagerService, IBackupAgent agent,
+    RestoreFileRunnable(UserBackupManagerService backupManagerService, IBackupAgent agent,
             FileMetadata info, ParcelFileDescriptor socket, int token) throws IOException {
         mAgent = agent;
         mInfo = info;
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index c933833..e465c7e 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -17,8 +17,8 @@
 package com.android.server.backup.utils;
 
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.annotation.Nullable;
 import android.app.backup.BackupTransport;
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 6dd5284..0f4b681 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -34,14 +34,14 @@
 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSIONS_MATCH;
 import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BACKUP_OLDER;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupManagerMonitor;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 6174300..784d398 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -32,6 +32,10 @@
         "android.hardware.tv.cec-V1.0-java",
     ],
 
+    required: [
+        "gps_debug.conf",
+    ],
+
     static_libs: [
         "time_zone_distro",
         "time_zone_distro_installer",
@@ -69,3 +73,9 @@
     name: "services.core",
     static_libs: ["services.core.priorityboosted"],
 }
+
+
+prebuilt_etc {
+    name: "gps_debug.conf",
+    src: "java/com/android/server/location/gps_debug.conf",
+}
diff --git a/services/core/java/com/android/server/AbstractMasterSystemService.java b/services/core/java/com/android/server/AbstractMasterSystemService.java
index 9c1e3cd..1759ce1 100644
--- a/services/core/java/com/android/server/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/AbstractMasterSystemService.java
@@ -93,6 +93,12 @@
     public boolean debug = false;
 
     /**
+     * Whether the service is allowed to bind to an instant-app.
+     */
+    @GuardedBy("mLock")
+    protected boolean mAllowInstantService;
+
+    /**
      * Users disabled due to {@link UserManager} restrictions, or {@code null} if the service cannot
      * be disabled through {@link UserManager}.
      */
@@ -176,6 +182,47 @@
     }
 
     /**
+     * Gets whether the service is allowed to bind to an instant-app.
+     *
+     * <p>Typically called by {@code ShellCommand} during CTS tests.
+     *
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    public final boolean getAllowInstantService() {
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            return mAllowInstantService;
+        }
+    }
+
+    /**
+     * Sets whether the service is allowed to bind to an instant-app.
+     *
+     * <p>Typically called by {@code ShellCommand} during CTS tests.
+     *
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    public final void setAllowInstantService(boolean mode) {
+        Slog.i(mTag, "setAllowInstantService(): " + mode);
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            mAllowInstantService = mode;
+        }
+    }
+
+    /**
+     * Asserts that the caller has permissions to manage this service.
+     *
+     * <p>Typically called by {@code ShellCommand} implementations.
+     *
+     * @throws UnsupportedOperationException if subclass doesn't override it.
+     * @throws SecurityException if caller is not allowed to manage this service's settings.
+     */
+    protected void enforceCallingPermissionForManagement() {
+        throw new UnsupportedOperationException("Not implemented by " + getClass());
+    }
+
+    /**
      * Creates a new service that will be added to the cache.
      *
      * @param resolvedUserId the resolved user id for the service.
@@ -362,6 +409,7 @@
             pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
             pw.print(" Verbose: "); pw.println(realVerbose);
             pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
+            pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
             pw.print(prefix); pw.print("Settings property: "); pw.println(
                     getServiceSettingsProperty());
             pw.print(prefix); pw.print("Cached services: ");
diff --git a/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java b/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
new file mode 100644
index 0000000..f532b22
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractMultiplePendingRequestsRemoteService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Base class representing a remote service that can queue multiple pending requests while not
+ * bound.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractMultiplePendingRequestsRemoteService<
+        S extends AbstractMultiplePendingRequestsRemoteService<S>>
+        extends AbstractRemoteService<S> {
+
+    private final int mInitialCapacity;
+
+    protected ArrayList<PendingRequest<S>> mPendingRequests;
+
+    public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context,
+            @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+            @NonNull VultureCallback callback, boolean bindInstantServiceAllowed, boolean verbose,
+            int initialCapacity) {
+        super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+                verbose);
+        mInitialCapacity = initialCapacity;
+    }
+
+    @Override // from AbstractRemoteService
+    void handlePendingRequests() {
+        if (mPendingRequests != null) {
+            final int size = mPendingRequests.size();
+            if (mVerbose) Slog.v(mTag, "Sending " + size + " pending requests");
+            for (int i = 0; i < size; i++) {
+                mPendingRequests.get(i).run();
+            }
+            mPendingRequests = null;
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    protected void handleOnDestroy() {
+        if (mPendingRequests != null) {
+            final int size = mPendingRequests.size();
+            if (mVerbose) Slog.v(mTag, "Canceling " + size + " pending requests");
+            for (int i = 0; i < size; i++) {
+                mPendingRequests.get(i).cancel();
+            }
+            mPendingRequests = null;
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        super.dump(prefix, pw);
+
+        pw.append(prefix).append("initialCapacity=").append(String.valueOf(mInitialCapacity))
+                .println();
+        final int size = mPendingRequests == null ? 0 : mPendingRequests.size();
+        pw.append(prefix).append("pendingRequests=").append(String.valueOf(size)).println();
+    }
+
+    @Override // from AbstractRemoteService
+    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+        if (mPendingRequests == null) {
+            mPendingRequests = new ArrayList<>(mInitialCapacity);
+        }
+        mPendingRequests.add(pendingRequest);
+        if (mVerbose) {
+            Slog.v(mTag, "queued " + mPendingRequests.size() + " requests; last=" + pendingRequest);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/AbstractPerUserSystemService.java b/services/core/java/com/android/server/AbstractPerUserSystemService.java
index 71d261c..001d85f 100644
--- a/services/core/java/com/android/server/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/AbstractPerUserSystemService.java
@@ -108,7 +108,8 @@
      * <p>Typically called when the service {@link Settings} property or {@link UserManager}
      * restriction changed, which includes the initial creation of the service.
      *
-     * <p>Subclasses can extend this method to provide extra initialization.
+     * <p>Subclasses can extend this method to provide extra initialization, like clearing up
+     * previous state.
      *
      * @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
      *
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 73a34d6..f636487 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -45,13 +45,20 @@
  *
  * <p>All state of this class is modified on a handler thread.
  *
+ * <p><b>NOTE: </b>this class should not be extended directly, you should extend either
+ * {@link AbstractSinglePendingRequestRemoteService} or
+ * {@link AbstractMultiplePendingRequestsRemoteService}.
+ *
  * <p>See {@code com.android.server.autofill.RemoteFillService} for a concrete
  * (no pun intended) example of how to use it.
  *
+ * @param <S> the concrete remote service class
+ *
  * @hide
  */
 //TODO(b/117779333): improve javadoc above instead of using Autofill as an example
-public abstract class AbstractRemoteService implements DeathRecipient {
+public abstract class AbstractRemoteService<S extends AbstractRemoteService<S>>
+        implements DeathRecipient {
 
     private static final int MSG_UNBIND = 1;
 
@@ -64,8 +71,6 @@
     protected final Handler mHandler;
     protected final ComponentName mComponentName;
 
-    protected PendingRequest<? extends AbstractRemoteService> mPendingRequest;
-
     private final Context mContext;
     private final Intent mIntent;
     private final VultureCallback mVultureCallback;
@@ -88,10 +93,11 @@
          *
          * @param service service that died!
          */
-        void onServiceDied(AbstractRemoteService service);
+        void onServiceDied(AbstractRemoteService<? extends AbstractRemoteService<?>> service);
     }
 
-    public AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
+    // NOTE: must be package-protected so this class is not extend outside
+    AbstractRemoteService(@NonNull Context context, @NonNull String serviceInterface,
             @NonNull ComponentName componentName, int userId, @NonNull VultureCallback callback,
             boolean bindInstantServiceAllowed, boolean verbose) {
         mContext = context;
@@ -118,12 +124,25 @@
         return mDestroyed;
     }
 
+    private void handleOnConnectedStateChangedInternal(boolean connected) {
+        if (connected) {
+            handlePendingRequests();
+        }
+        handleOnConnectedStateChanged(connected);
+    }
+
     /**
-     * Callback called when the system connected / disconnected to the service.
+     * Handles the pending requests when the connection it bounds to the remote service.
+     */
+    abstract void handlePendingRequests();
+
+    /**
+     * Callback called when the system connected / disconnected to the service and the pending
+     * requests have been handled.
      *
      * @param state {@code true} when connected, {@code false} when disconnected.
      */
-    protected void onConnectedStateChanged(boolean state) {
+    protected void handleOnConnectedStateChanged(boolean state) {
     }
 
     /**
@@ -144,14 +163,18 @@
 
     private void handleDestroy() {
         if (checkIfDestroyed()) return;
-        if (mPendingRequest != null) {
-            mPendingRequest.cancel();
-            mPendingRequest = null;
-        }
-        ensureUnbound();
+        handleOnDestroy();
+        handleEnsureUnbound();
         mDestroyed = true;
     }
 
+    /**
+     * Clears the state when this object is destroyed.
+     *
+     * <p>Typically used to cancel the pending requests.
+     */
+    protected abstract void handleOnDestroy();
+
     @Override // from DeathRecipient
     public void binderDied() {
         mHandler.sendMessage(obtainMessage(AbstractRemoteService::handleBinderDied, this));
@@ -183,9 +206,7 @@
         pw.append(prefix).append(tab).append("destroyed=")
                 .append(String.valueOf(mDestroyed)).println();
         pw.append(prefix).append(tab).append("bound=")
-                .append(String.valueOf(isBound())).println();
-        pw.append(prefix).append(tab).append("hasPendingRequest=")
-                .append(String.valueOf(mPendingRequest != null)).println();
+                .append(String.valueOf(handleIsBound())).println();
         pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed);
         pw.append(prefix).append("idleTimeout=")
             .append(Long.toString(getTimeoutIdleBindMillis() / 1000)).append("s").println();
@@ -194,7 +215,7 @@
         pw.println();
     }
 
-    protected void scheduleRequest(PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+    protected void scheduleRequest(@NonNull PendingRequest<S> pendingRequest) {
         mHandler.sendMessage(obtainMessage(
                 AbstractRemoteService::handlePendingRequest, this, pendingRequest));
     }
@@ -215,19 +236,20 @@
     private void handleUnbind() {
         if (checkIfDestroyed()) return;
 
-        ensureUnbound();
+        handleEnsureUnbound();
     }
 
-    private void handlePendingRequest(
-            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
+    /**
+     * Handles a request, either processing it right now when bound, or saving it to be handled when
+     * bound.
+     */
+    protected final void handlePendingRequest(@NonNull PendingRequest<S> pendingRequest) {
         if (checkIfDestroyed() || mCompleted) return;
 
-        if (!isBound()) {
-            if (mPendingRequest != null) {
-                mPendingRequest.cancel();
-            }
-            mPendingRequest = pendingRequest;
-            ensureBound();
+        if (!handleIsBound()) {
+            if (mVerbose) Slog.v(mTag, "handlePendingRequest(): queuing" + pendingRequest);
+            handlePendingRequestWhileUnBound(pendingRequest);
+            handleEnsureBound();
         } else {
             if (mVerbose) Slog.v(mTag, "handlePendingRequest(): " + pendingRequest);
             pendingRequest.run();
@@ -237,12 +259,17 @@
         }
     }
 
-    private boolean isBound() {
+    /**
+     * Defines what to do with a request that arrives while not bound to the service.
+     */
+    abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest);
+
+    private boolean handleIsBound() {
         return mServiceInterface != null;
     }
 
-    private void ensureBound() {
-        if (isBound() || mBinding) return;
+    private void handleEnsureBound() {
+        if (handleIsBound() || mBinding) return;
 
         if (mVerbose) Slog.v(mTag, "ensureBound()");
         mBinding = true;
@@ -253,7 +280,7 @@
         }
 
         final boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
-                new UserHandle(mUserId));
+                mHandler, new UserHandle(mUserId));
 
         if (!willBind) {
             Slog.w(mTag, "could not bind to " + mIntent + " using flags " + flags);
@@ -265,13 +292,13 @@
         }
     }
 
-    private void ensureUnbound() {
-        if (!isBound() && !mBinding) return;
+    private void handleEnsureUnbound() {
+        if (!handleIsBound() && !mBinding) return;
 
         if (mVerbose) Slog.v(mTag, "ensureUnbound()");
         mBinding = false;
-        if (isBound()) {
-            onConnectedStateChanged(false);
+        if (handleIsBound()) {
+            handleOnConnectedStateChangedInternal(false);
             if (mServiceInterface != null) {
                 mServiceInterface.asBinder().unlinkToDeath(this, 0);
                 mServiceInterface = null;
@@ -283,6 +310,7 @@
     private class RemoteServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mVerbose) Slog.v(mTag, "onServiceConnected()");
             if (mDestroyed || !mBinding) {
                 // This is abnormal. Unbinding the connection has been requested already.
                 Slog.wtf(mTag, "onServiceConnected() was dispatched after unbindService.");
@@ -296,15 +324,7 @@
                 handleBinderDied();
                 return;
             }
-            onConnectedStateChanged(true);
-
-            if (mPendingRequest != null) {
-                final PendingRequest<? extends AbstractRemoteService> pendingRequest =
-                        mPendingRequest;
-                mPendingRequest = null;
-                handlePendingRequest(pendingRequest);
-            }
-
+            handleOnConnectedStateChangedInternal(true);
             mServiceDied = false;
         }
 
@@ -325,25 +345,12 @@
         return mDestroyed;
     }
 
-    protected boolean handleResponseCallbackCommon(
-            PendingRequest<? extends AbstractRemoteService> pendingRequest) {
-        if (isDestroyed()) return false;
-
-        if (mPendingRequest == pendingRequest) {
-            mPendingRequest = null;
-        }
-        if (mPendingRequest == null) {
-            scheduleUnbind();
-        }
-        return true;
-    }
-
     /**
      * Base class for the requests serviced by the remote service.
      *
      * @param <S> the remote service class
      */
-    public abstract static class PendingRequest<S extends AbstractRemoteService>
+    public abstract static class PendingRequest<S extends AbstractRemoteService<S>>
             implements Runnable {
         protected final String mTag = getClass().getSimpleName();
         protected final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java b/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
new file mode 100644
index 0000000..8e1f540
--- /dev/null
+++ b/services/core/java/com/android/server/AbstractSinglePendingRequestRemoteService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+
+/**
+ * Base class representing a remote service that can have only one pending requests while not bound.
+ *
+ * <p>If another request is received while not bound, the previous one will be canceled.
+ *
+ * @param <S> the concrete remote service class
+ *
+ * @hide
+ */
+public abstract class AbstractSinglePendingRequestRemoteService<
+        S extends AbstractSinglePendingRequestRemoteService<S>> extends AbstractRemoteService<S> {
+
+    protected PendingRequest<S> mPendingRequest;
+
+    public AbstractSinglePendingRequestRemoteService(@NonNull Context context,
+            @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId,
+            @NonNull VultureCallback callback, boolean bindInstantServiceAllowed,
+            boolean verbose) {
+        super(context, serviceInterface, componentName, userId, callback, bindInstantServiceAllowed,
+                verbose);
+    }
+
+    @Override // from AbstractRemoteService
+    void handlePendingRequests() {
+        if (mPendingRequest != null) {
+            final PendingRequest<S> pendingRequest = mPendingRequest;
+            mPendingRequest = null;
+            handlePendingRequest(pendingRequest);
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    protected void handleOnDestroy() {
+        if (mPendingRequest != null) {
+            mPendingRequest.cancel();
+            mPendingRequest = null;
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
+        super.dump(prefix, pw);
+        pw.append(prefix).append("hasPendingRequest=")
+                .append(String.valueOf(mPendingRequest != null)).println();
+    }
+
+    @Override // from AbstractRemoteService
+    void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S> pendingRequest) {
+        if (mPendingRequest != null) {
+            if (mVerbose) {
+                Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest);
+            }
+            mPendingRequest.cancel();
+        }
+        mPendingRequest = pendingRequest;
+    }
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 356a4da..8d912fa 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -692,7 +692,7 @@
                     }
                 });
 
-        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (!StorageManager.hasIsolatedStorage()) {
             StorageManagerInternal storageManagerInternal = LocalServices.getService(
                     StorageManagerInternal.class);
             storageManagerInternal.addExternalStoragePolicy(
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index dd96075..81f0259 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -56,6 +56,7 @@
         private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
         private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
         private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+        private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
 
         private boolean mEnabled;
         private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
@@ -97,6 +98,9 @@
             mBinderCallsStats.setSamplingInterval(mParser.getInt(
                     SETTINGS_SAMPLING_INTERVAL_KEY,
                     BinderCallsStats.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+            mBinderCallsStats.setMaxBinderCallStats(mParser.getInt(
+                    SETTINGS_MAX_CALL_STATS_KEY,
+                    BinderCallsStats.MAX_BINDER_CALL_STATS_COUNT_DEFAULT));
 
 
             final boolean enabled =
@@ -112,6 +116,7 @@
                 }
                 mEnabled = enabled;
                 mBinderCallsStats.reset();
+                mBinderCallsStats.setAddDebugEntries(enabled);
             }
         }
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 564d35a..bb3b9f7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1667,6 +1667,24 @@
                 loge("Error parsing ip address in validation event");
             }
         }
+
+        @Override
+        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
+                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+            NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
+            // Netd event only allow registrants from system. Each NetworkMonitor thread is under
+            // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
+            // event callback for certain nai. e.g. cellular. Register here to pass to
+            // NetworkMonitor instead.
+            // TODO: Move the Dns Event to NetworkMonitor. Use Binder.clearCallingIdentity() in
+            // registerNetworkAgent to have NetworkMonitor created with system process as design
+            // expectation. Also, NetdEventListenerService only allow one callback from each
+            // caller type. Need to re-factor NetdEventListenerService to allow multiple
+            // NetworkMonitor registrants.
+            if (nai != null && nai.satisfies(mDefaultRequest)) {
+                nai.networkMonitor.sendMessage(NetworkMonitor.EVENT_DNS_NOTIFICATION, returnCode);
+            }
+        }
     };
 
     @VisibleForTesting
@@ -2081,12 +2099,7 @@
         return new MockableSystemProperties();
     }
 
-    private void updateTcpBufferSizes(NetworkAgentInfo nai) {
-        if (isDefaultNetwork(nai) == false) {
-            return;
-        }
-
-        String tcpBufferSizes = nai.linkProperties.getTcpBufferSizes();
+    private void updateTcpBufferSizes(String tcpBufferSizes) {
         String[] values = null;
         if (tcpBufferSizes != null) {
             values = tcpBufferSizes.split(",");
@@ -4762,8 +4775,8 @@
         updateUids(nai, null, nai.networkCapabilities);
     }
 
-    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
-        LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+            LinkProperties oldLp) {
         int netId = networkAgent.network.netId;
 
         // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4778,7 +4791,9 @@
 //        for (LinkProperties lp : newLp.getStackedLinks()) {
 //            updateMtu(lp, null);
 //        }
-        updateTcpBufferSizes(networkAgent);
+        if (isDefaultNetwork(networkAgent)) {
+            updateTcpBufferSizes(newLp.getTcpBufferSizes());
+        }
 
         updateRoutes(newLp, oldLp, netId);
         updateDnses(newLp, oldLp, netId);
@@ -4788,8 +4803,6 @@
         // updateDnses will fetch the private DNS configuration from DnsManager.
         mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
-        // Start or stop clat accordingly to network state.
-        networkAgent.updateClat(mNMS);
         if (isDefaultNetwork(networkAgent)) {
             handleApplyDefaultProxy(newLp.getHttpProxy());
         } else {
@@ -4800,8 +4813,12 @@
             synchronized (networkAgent) {
                 networkAgent.linkProperties = newLp;
             }
+            // Start or stop clat accordingly to network state.
+            networkAgent.updateClat(mNMS);
             notifyIfacesChangedForNetworkStats();
-            notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            if (networkAgent.everConnected) {
+                notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
+            }
         }
 
         mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
@@ -5106,13 +5123,7 @@
                     "; created=" + nai.created +
                     "; everConnected=" + nai.everConnected);
         }
-        LinkProperties oldLp = nai.linkProperties;
-        synchronized (nai) {
-            nai.linkProperties = newLp;
-        }
-        if (nai.everConnected) {
-            updateLinkProperties(nai, oldLp);
-        }
+        updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
 
     private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
@@ -5273,7 +5284,7 @@
 
         notifyLockdownVpn(newNetwork);
         handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
-        updateTcpBufferSizes(newNetwork);
+        updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
     }
@@ -5688,7 +5699,8 @@
             }
 
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
-            updateLinkProperties(networkAgent, null);
+            updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
+                    null);
 
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             scheduleUnvalidatedPrompt(networkAgent);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 7ee3d3b..126bf65 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1236,7 +1236,8 @@
                     OsConstants.UDP_ENCAP,
                     OsConstants.UDP_ENCAP_ESPINUDP);
 
-            mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid);
+            mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(
+                        new ParcelFileDescriptor(sockFd), callingUid);
             if (port != 0) {
                 Log.v(TAG, "Binding to port " + port);
                 Os.bind(sockFd, INADDR_ANY, port);
@@ -1696,7 +1697,7 @@
         mSrvConfig
                 .getNetdInstance()
                 .ipSecApplyTransportModeTransform(
-                        socket.getFileDescriptor(),
+                        socket,
                         callingUid,
                         direction,
                         c.getSourceAddress(),
@@ -1715,7 +1716,7 @@
             throws RemoteException {
         mSrvConfig
                 .getNetdInstance()
-                .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
+                .ipSecRemoveTransportModeTransform(socket);
     }
 
     /**
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e5275e5..cc7bf33 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -19,7 +19,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
-import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -85,7 +84,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
@@ -114,7 +112,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.NoSuchElementException;
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -3413,48 +3410,6 @@
         }
     }
 
-    @Override
-    public PendingIntent createManageLocationPermissionIntent(String packageName,
-            String permission) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkArgument(permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)
-                || permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)
-                || permission.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION));
-
-        int callingUid = Binder.getCallingUid();
-        long token = Binder.clearCallingIdentity();
-        try {
-            String locProvider = getNetworkProviderPackage();
-            if (locProvider == null) {
-                return null;
-            }
-
-            PackageInfo locProviderInfo;
-            try {
-                locProviderInfo = mContext.getPackageManager().getPackageInfo(
-                        locProvider, PackageManager.MATCH_DIRECT_BOOT_AUTO);
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Could not resolve " + locProvider, e);
-                return null;
-            }
-
-            if (locProviderInfo.applicationInfo.uid != callingUid) {
-                throw new SecurityException("Only " + locProvider + " can call this API");
-            }
-
-            Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
-            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-            intent.putExtra(Intent.EXTRA_PERMISSION_NAME, permission);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            return PendingIntent.getActivity(mContext,
-                    Objects.hash(packageName, permission), intent,
-                    PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     private void log(String log) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Slog.d(TAG, log);
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
index c563ad2..fa3baba 100644
--- a/services/core/java/com/android/server/LooperStatsService.java
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -94,6 +94,8 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
         pw.print("Start time: ");
         pw.println(DateFormat.format("yyyy-MM-dd HH:mm:ss", mStats.getStartTimeMillis()));
+        pw.print("On battery time (ms): ");
+        pw.println(mStats.getBatteryTimeMillis());
         final List<LooperStats.ExportedEntry> entries = mStats.getEntries();
         entries.sort(Comparator
                 .comparing((LooperStats.ExportedEntry entry) -> entry.workSourceUid)
diff --git a/services/core/java/com/android/server/RuntimeService.java b/services/core/java/com/android/server/RuntimeService.java
new file mode 100644
index 0000000..ccfac80
--- /dev/null
+++ b/services/core/java/com/android/server/RuntimeService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.content.Context;
+import android.os.Binder;
+import android.service.runtime.DebugEntryProto;
+import android.service.runtime.RuntimeServiceInfoProto;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import libcore.timezone.TimeZoneDataFiles;
+import libcore.util.CoreLibraryDebug;
+import libcore.util.DebugInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.timezone.distro.DistroException;
+import com.android.timezone.distro.DistroVersion;
+import com.android.timezone.distro.FileUtils;
+import com.android.timezone.distro.TimeZoneDistro;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * This service exists only as a "dumpsys" target which reports information about the status of the
+ * runtime and related libraries.
+ */
+public class RuntimeService extends Binder {
+
+    private static final String TAG = "RuntimeService";
+
+    private final Context mContext;
+
+    public RuntimeService(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
+            return;
+        }
+
+        boolean protoFormat = hasOption(args, "--proto");
+        ProtoOutputStream proto = null;
+
+        DebugInfo coreLibraryDebugInfo = CoreLibraryDebug.getDebugInfo();
+        addTimeZoneApkDebugInfo(coreLibraryDebugInfo);
+
+        if (protoFormat) {
+            proto = new ProtoOutputStream(fd);
+            reportTimeZoneInfoProto(coreLibraryDebugInfo, proto);
+        } else {
+            reportTimeZoneInfo(coreLibraryDebugInfo, pw);
+        }
+
+        if (protoFormat) {
+            proto.flush();
+        }
+    }
+
+    /** Returns {@code true} if {@code args} contains {@code arg}. */
+    private static boolean hasOption(String[] args, String arg) {
+        for (String opt : args) {
+            if (arg.equals(opt)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Add information to {@link DebugInfo} about the time zone data supplied by the
+     * "Time zone updates via APK" feature.
+     */
+    private static void addTimeZoneApkDebugInfo(DebugInfo coreLibraryDebugInfo) {
+        // Add /data tz data set using the DistroVersion class (which libcore cannot use).
+        // This update mechanism will be removed after the time zone APEX is launched so this
+        // untidiness will disappear with it.
+        String debugKeyPrefix = "core_library.timezone.data_";
+        String versionFileName = TimeZoneDataFiles.getDataTimeZoneFile(
+                TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
+        addDistroVersionDebugInfo(versionFileName, debugKeyPrefix, coreLibraryDebugInfo);
+    }
+
+    /**
+     * Prints {@code coreLibraryDebugInfo} to {@code pw}.
+     *
+     * <p>If you change this method, make sure to modify
+     * {@link #reportTimeZoneInfoProto(DebugInfo, ProtoOutputStream)} as well.
+     */
+    private static void reportTimeZoneInfo(DebugInfo coreLibraryDebugInfo,
+            PrintWriter pw) {
+        pw.println("Core Library Debug Info: ");
+        for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+            pw.print(debugEntry.getKey());
+            pw.print(": \"");
+            pw.print(debugEntry.getStringValue());
+            pw.println("\"");
+        }
+    }
+
+    /**
+     * Adds {@code coreLibraryDebugInfo} to {@code protoStream}.
+     *
+     * <p>If you change this method, make sure to modify
+     * {@link #reportTimeZoneInfo(DebugInfo, PrintWriter)}.
+     */
+    private static void reportTimeZoneInfoProto(
+            DebugInfo coreLibraryDebugInfo, ProtoOutputStream protoStream) {
+        for (DebugInfo.DebugEntry debugEntry : coreLibraryDebugInfo.getDebugEntries()) {
+            long entryToken = protoStream.start(RuntimeServiceInfoProto.DEBUG_ENTRY);
+            protoStream.write(DebugEntryProto.KEY, debugEntry.getKey());
+            protoStream.write(DebugEntryProto.STRING_VALUE, debugEntry.getStringValue());
+            protoStream.end(entryToken);
+        }
+    }
+
+    /**
+     * Adds version information to {@code debugInfo} from the distro_version file that may exist
+     * at {@code distroVersionFileName}. If the file does not exist or cannot be read this is
+     * reported as debug information too.
+     */
+    private static void addDistroVersionDebugInfo(String distroVersionFileName,
+            String debugKeyPrefix, DebugInfo debugInfo) {
+        File file = new File(distroVersionFileName);
+        String statusKey = debugKeyPrefix + "status";
+        if (file.exists()) {
+            try {
+                byte[] versionBytes =
+                        FileUtils.readBytes(file, DistroVersion.DISTRO_VERSION_FILE_LENGTH);
+                DistroVersion distroVersion = DistroVersion.fromBytes(versionBytes);
+                String formatVersionString = distroVersion.formatMajorVersion + "."
+                        + distroVersion.formatMinorVersion;
+                debugInfo.addStringEntry(statusKey, "OK")
+                        .addStringEntry(debugKeyPrefix + "formatVersion", formatVersionString)
+                        .addStringEntry(debugKeyPrefix + "rulesVersion",
+                                distroVersion.rulesVersion)
+                        .addStringEntry(debugKeyPrefix + "revision",
+                                distroVersion.revision);
+            } catch (IOException | DistroException e) {
+                debugInfo.addStringEntry(statusKey, "ERROR");
+                debugInfo.addStringEntry(debugKeyPrefix + "exception_class",
+                        e.getClass().getName());
+                debugInfo.addStringEntry(debugKeyPrefix + "exception_msg", e.getMessage());
+                logMessage("Error reading " + file, e);
+            }
+        } else {
+            debugInfo.addStringEntry(statusKey, "NOT_FOUND");
+        }
+    }
+
+    private static void logMessage(String msg, Throwable t) {
+        Slog.v(TAG, msg, t);
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 923ac00..390126c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -99,6 +99,7 @@
 import android.os.storage.VolumeRecord;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -183,8 +184,7 @@
     private static final String ZRAM_ENABLED_PROPERTY =
             "persist.sys.zram_enabled";
 
-    private static final boolean ENABLE_ISOLATED_STORAGE = SystemProperties
-            .getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false);
+    private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
 
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
@@ -1016,7 +1016,7 @@
 
         // On an encrypted device we can't see system properties yet, so pull
         // the system locale out of the mount service.
-        if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+        if ("".equals(VoldProperties.encrypt_progress().orElse(""))) {
             copyLocaleFromMountService();
         }
     }
@@ -2686,24 +2686,35 @@
     class AppFuseMountScope extends AppFuseBridge.MountScope {
         boolean opened = false;
 
-        public AppFuseMountScope(int uid, int pid, int mountId) {
-            super(uid, pid, mountId);
+        public AppFuseMountScope(int uid, int mountId) {
+            super(uid, mountId);
         }
 
         @Override
         public ParcelFileDescriptor open() throws NativeDaemonConnectorException {
             try {
                 return new ParcelFileDescriptor(
-                        mVold.mountAppFuse(uid, Process.myPid(), mountId));
+                        mVold.mountAppFuse(uid, mountId));
             } catch (Exception e) {
                 throw new NativeDaemonConnectorException("Failed to mount", e);
             }
         }
 
         @Override
+        public ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+                throws NativeDaemonConnectorException {
+            try {
+                return new ParcelFileDescriptor(
+                        mVold.openAppFuseFile(uid, mountId, fileId, flags));
+            } catch (Exception e) {
+                throw new NativeDaemonConnectorException("Failed to open", e);
+            }
+        }
+
+        @Override
         public void close() throws Exception {
             if (opened) {
-                mVold.unmountAppFuse(uid, Process.myPid(), mountId);
+                mVold.unmountAppFuse(uid, mountId);
                 opened = false;
             }
         }
@@ -2713,7 +2724,6 @@
     public @Nullable AppFuseMount mountProxyFileDescriptorBridge() {
         Slog.v(TAG, "mountProxyFileDescriptorBridge");
         final int uid = Binder.getCallingUid();
-        final int pid = Binder.getCallingPid();
 
         while (true) {
             synchronized (mAppFuseLock) {
@@ -2727,7 +2737,7 @@
                     final int name = mNextAppFuseName++;
                     try {
                         return new AppFuseMount(
-                            name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name)));
+                            name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, name)));
                     } catch (FuseUnavailableMountException e) {
                         if (newlyCreated) {
                             // If newly created bridge fails, it's a real error.
@@ -2748,14 +2758,13 @@
     public @Nullable ParcelFileDescriptor openProxyFileDescriptor(
             int mountId, int fileId, int mode) {
         Slog.v(TAG, "mountProxyFileDescriptor");
-        final int pid = Binder.getCallingPid();
         try {
             synchronized (mAppFuseLock) {
                 if (mAppFuseBridge == null) {
                     Slog.e(TAG, "FuseBridge has not been created");
                     return null;
                 }
-                return mAppFuseBridge.openFile(pid, mountId, fileId, mode);
+                return mAppFuseBridge.openFile(mountId, fileId, mode);
             }
         } catch (FuseUnavailableMountException | InterruptedException error) {
             Slog.v(TAG, "The mount point has already been invalid", error);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f0b472b..a2cbfaa 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -47,6 +47,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.util.LocalLog;
 import android.util.StatsLog;
 
@@ -1664,6 +1665,14 @@
     }
 
     @Override
+    public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
+        // TODO checkPermission, modify Listener constent documentation
+        // TODO implement multisim emergency number list update in listener
+        // TODO implement PhoneStateListenerTest
+    }
+
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
new file mode 100644
index 0000000..629e882
--- /dev/null
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.app.ActivityThread;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.util.Slog;
+
+/**
+ * Receiver responsible for updating the wallpaper when the device
+ * configuration has changed.
+ *
+ * @hide
+ */
+public class WallpaperUpdateReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "WallpaperUpdateReceiver";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public void onReceive(final Context context, final Intent intent) {
+        if (DEBUG) Slog.d(TAG, "onReceive: " + intent);
+
+        if (intent != null && Intent.ACTION_DEVICE_CUSTOMIZATION_READY.equals(intent.getAction())) {
+            AsyncTask.execute(this::updateWallpaper);
+        }
+    }
+
+    private void updateWallpaper() {
+        try {
+            ActivityThread currentActivityThread = ActivityThread.currentActivityThread();
+            Context uiContext = currentActivityThread.getSystemUiContext();
+            WallpaperManager wallpaperManager = WallpaperManager.getInstance(uiContext);
+            if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper.");
+            Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+            // set a blank wallpaper to force a redraw of default_wallpaper
+            wallpaperManager.setBitmap(blank);
+            wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper);
+        } catch (Exception e) {
+            Slog.w(TAG, "Failed to customize system wallpaper." + e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index d1b56e9..e80e9e1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -105,6 +105,7 @@
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.graphics.allocator@2.0::IAllocator",
         "android.hardware.graphics.composer@2.1::IComposer",
+        "android.hardware.health@2.0::IHealth",
         "android.hardware.media.omx@1.0::IOmx",
         "android.hardware.media.omx@1.0::IOmxStore",
         "android.hardware.sensors@1.0::ISensors",
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5c77f0a..8571ae6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
+
 import android.content.ContentResolver;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -26,8 +28,6 @@
 
 import java.io.PrintWriter;
 
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;
-
 /**
  * Settings constants that can modify the activity manager's behavior.
  */
@@ -222,6 +222,10 @@
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
 
+    // Indicates whether the background activity starts is enabled.
+    // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED
+    volatile boolean mFlagBackgroundActivityStartsEnabled;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -256,6 +260,10 @@
     private static final Uri ACTIVITY_STARTS_LOGGING_ENABLED_URI = Settings.Global.getUriFor(
                 Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED);
 
+    private static final Uri BACKGROUND_ACTIVITY_STARTS_ENABLED_URI =
+                Settings.Global.getUriFor(
+                        Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED);
+
     public ActivityManagerConstants(ActivityManagerService service, Handler handler) {
         super(handler);
         mService = service;
@@ -266,8 +274,10 @@
         mResolver = resolver;
         mResolver.registerContentObserver(ACTIVITY_MANAGER_CONSTANTS_URI, false, this);
         mResolver.registerContentObserver(ACTIVITY_STARTS_LOGGING_ENABLED_URI, false, this);
+        mResolver.registerContentObserver(BACKGROUND_ACTIVITY_STARTS_ENABLED_URI, false, this);
         updateConstants();
         updateActivityStartsLoggingEnabled();
+        updateBackgroundActivityStartsEnabled();
     }
 
     public void setOverrideMaxCachedProcesses(int value) {
@@ -290,6 +300,8 @@
             updateConstants();
         } else if (ACTIVITY_STARTS_LOGGING_ENABLED_URI.equals(uri)) {
             updateActivityStartsLoggingEnabled();
+        } else if (BACKGROUND_ACTIVITY_STARTS_ENABLED_URI.equals(uri)) {
+            updateBackgroundActivityStartsEnabled();
         }
     }
 
@@ -373,6 +385,11 @@
                 Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1;
     }
 
+    private void updateBackgroundActivityStartsEnabled() {
+        mFlagBackgroundActivityStartsEnabled = Settings.Global.getInt(mResolver,
+                Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED, 1) == 1;
+    }
+
     private void updateMaxCachedProcesses() {
         CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0
                 ? MAX_CACHED_PROCESSES : mOverrideMaxCachedProcesses;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5f9e349..8842f41 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -272,6 +272,7 @@
 import android.os.WorkSource;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
+import android.sysprop.VoldProperties;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.SuggestionSpan;
@@ -328,6 +329,7 @@
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
+import com.android.server.DisplayThread;
 import com.android.server.IntentResolver;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
@@ -665,16 +667,50 @@
     final class PidMap {
         private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
 
+        /**
+         * Puts the process record in the map.
+         * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+         * method.
+         */
         void put(int key, ProcessRecord value) {
-            mPidMap.put(key, value);
+            synchronized (this) {
+                mPidMap.put(key, value);
+            }
             mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
         }
 
+        /**
+         * Removes the process record from the map.
+         * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+         * method.
+         */
         void remove(int pid) {
-            mPidMap.remove(pid);
+            synchronized (this) {
+                mPidMap.remove(pid);
+            }
             mAtmInternal.onProcessUnMapped(pid);
         }
 
+        /**
+         * Removes the process record from the map if it has a thread.
+         * <p>NOTE: Callers should avoid acquiring the mPidsSelfLocked lock before calling this
+         * method.
+         */
+        boolean removeIfNoThread(int pid) {
+            boolean removed = false;
+            synchronized (this) {
+                final ProcessRecord app = get(pid);
+                if (app != null && app.thread == null) {
+                    mPidMap.remove(pid);
+                    removed = true;
+                }
+            }
+            if (removed) {
+                mAtmInternal.onProcessUnMapped(pid);
+            }
+            return removed;
+        }
+
         ProcessRecord get(int pid) {
             return mPidMap.get(pid);
         }
@@ -1888,9 +1924,7 @@
                 app.getWindowProcessController().setPid(MY_PID);
                 app.maxAdj = ProcessList.SYSTEM_ADJ;
                 app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
-                synchronized (mPidsSelfLocked) {
-                    mPidsSelfLocked.put(app.pid, app);
-                }
+                mPidsSelfLocked.put(app.pid, app);
                 mProcessList.updateLruProcessLocked(app, false, null);
                 updateOomAdjLocked();
             }
@@ -2262,7 +2296,8 @@
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
 
         mActivityTaskManager = atm;
-        mActivityTaskManager.setActivityManagerService(mIntentFirewall, mPendingIntentController);
+        mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
+                DisplayThread.get().getLooper());
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
 
         mProcessCpuThread = new Thread("CpuTracker") {
@@ -4252,14 +4287,7 @@
 
     private final void processStartTimedOutLocked(ProcessRecord app) {
         final int pid = app.pid;
-        boolean gone = false;
-        synchronized (mPidsSelfLocked) {
-            ProcessRecord knownApp = mPidsSelfLocked.get(pid);
-            if (knownApp != null && knownApp.thread == null) {
-                mPidsSelfLocked.remove(pid);
-                gone = true;
-            }
-        }
+        boolean gone = mPidsSelfLocked.removeIfNoThread(pid);
 
         if (gone) {
             Slog.w(TAG, "Process " + app + " failed to attach");
@@ -4372,7 +4400,6 @@
 
         EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
 
-        app.makeActive(thread, mProcessStats);
         app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
         app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
         app.forcingToImportant = null;
@@ -4579,6 +4606,10 @@
                 profilerInfo.closeFd();
                 profilerInfo = null;
             }
+
+            // Make app active after binding application or client may be running requests (e.g
+            // starting activities) before it is ready.
+            app.makeActive(thread, mProcessStats);
             checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
             mProcessList.updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -4777,8 +4808,8 @@
             SystemProperties.set("sys.boot_completed", "1");
 
             // And trigger dev.bootcomplete if we are not showing encryption progress
-            if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt"))
-                    || "".equals(SystemProperties.get("vold.encrypt_progress"))) {
+            if (!"trigger_restart_min_framework".equals(VoldProperties.decrypt().orElse(""))
+                    || "".equals(VoldProperties.encrypt_progress().orElse(""))) {
                 SystemProperties.set("dev.bootcomplete", "1");
             }
             mUserController.sendBootCompleted(
@@ -13108,11 +13139,8 @@
             return true;
         } else if (app.pid > 0 && app.pid != MY_PID) {
             // Goodbye!
-            boolean removed;
-            synchronized (mPidsSelfLocked) {
-                mPidsSelfLocked.remove(app.pid);
-                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-            }
+            mPidsSelfLocked.remove(app.pid);
+            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
             if (app.isolated) {
                 mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
@@ -18909,7 +18937,6 @@
                     if (memoryStat == null) {
                         continue;
                     }
-                    // TODO(rslawik): Delete RSS high-water mark field.
                     ProcessMemoryState processMemoryState =
                             new ProcessMemoryState(uid,
                                     r.processName,
@@ -18919,7 +18946,6 @@
                                     memoryStat.rssInBytes,
                                     memoryStat.cacheInBytes,
                                     memoryStat.swapInBytes,
-                                    memoryStat.rssHighWatermarkInBytes,
                                     memoryStat.startTimeNanos);
                     processMemoryStates.add(processMemoryState);
                 }
@@ -19201,6 +19227,10 @@
             return mConstants.mFlagActivityStartsLoggingEnabled;
         }
 
+        public boolean isBackgroundActivityStartsEnabled() {
+            return mConstants.mFlagBackgroundActivityStartsEnabled;
+        }
+
         public void reportCurKeyguardUsageEvent(boolean keyguardShowing) {
             synchronized(ActivityManagerService.this) {
                 ActivityManagerService.this.reportGlobalUsageEventLocked(keyguardShowing
@@ -19341,7 +19371,7 @@
 
         @Override
         public boolean isAppStorageSandboxed(int pid, int uid) {
-            if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+            if (!StorageManager.hasIsolatedStorage()) {
                 return false;
             }
             if (uid == SHELL_UID || uid == ROOT_UID) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 2541352..24543b7 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -25,10 +25,12 @@
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.BatteryStats;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
+import android.os.ThreadLocalWorkSource;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.IntArray;
@@ -43,11 +45,9 @@
 import libcore.util.EmptyArray;
 
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -74,7 +74,12 @@
     private final ScheduledExecutorService mExecutorService =
             Executors.newSingleThreadScheduledExecutor(
                     (ThreadFactory) r -> {
-                        Thread t = new Thread(r, "batterystats-worker");
+                        Thread t = new Thread(
+                                () -> {
+                                    ThreadLocalWorkSource.setUid(Process.myUid());
+                                    r.run();
+                                },
+                                "batterystats-worker");
                         t.setPriority(Thread.NORM_PRIORITY);
                         return t;
                     });
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index a0977be..8c39d75 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -58,8 +58,9 @@
 /**
  * BROADCASTS
  *
- * We keep two broadcast queues and associated bookkeeping, one for those at
- * foreground priority, and one for normal (background-priority) broadcasts.
+ * We keep three broadcast queues and associated bookkeeping, one for those at
+ * foreground priority, and one for normal (background-priority) broadcasts, and one to
+ * offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
  */
 public final class BroadcastQueue {
     private static final String TAG = "BroadcastQueue";
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index cc3da1c..90fe30c 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -49,13 +49,8 @@
     private static final boolean DEVICE_HAS_PER_APP_MEMCG =
             SystemProperties.getBoolean("ro.config.per_app_memcg", false);
 
-    /** Path to check if device has memcg */
-    private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
     /** Path to memory stat file for logging app start memory state */
     private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
-    /** Path to memory max usage file for logging app memory state */
-    private static final String MEMORY_MAX_USAGE_FILE_FMT =
-            "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes";
     /** Path to procfs stat file for logging app start memory state */
     private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
     /** Path to procfs status file for logging app memory state */
@@ -98,14 +93,7 @@
     @Nullable
     static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
         final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
-        MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath));
-        if (stat == null) {
-            return null;
-        }
-        String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid);
-        stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg(
-                readFileContents(maxUsagePath));
-        return stat;
+        return parseMemoryStatFromMemcg(readFileContents(statPath));
     }
 
     /**
@@ -116,12 +104,7 @@
     @Nullable
     public static MemoryStat readMemoryStatFromProcfs(int pid) {
         final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
-        MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
-        if (stat == null) {
-            return null;
-        }
-        stat.rssHighWatermarkInBytes = readRssHighWaterMarkFromProcfs(pid);
-        return stat;
+        return parseMemoryStatFromProcfs(readFileContents(statPath));
     }
 
     /**
@@ -185,19 +168,6 @@
         return memoryStat;
     }
 
-    @VisibleForTesting
-    static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) {
-        if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
-            return 0;
-        }
-        try {
-            return Long.parseLong(memoryMaxUsageContents);
-        } catch (NumberFormatException e) {
-            Slog.e(TAG, "Failed to parse value", e);
-            return 0;
-        }
-    }
-
     /**
      * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
      */
@@ -258,8 +228,6 @@
         public long cacheInBytes;
         /** Number of bytes of swap usage */
         public long swapInBytes;
-        /** Number of bytes of peak anonymous and swap cache memory */
-        public long rssHighWatermarkInBytes;
         /** Device time when the processes started. */
         public long startTimeNanos;
     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4b19398..62f1009 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,7 +27,6 @@
 import static android.os.Process.getTotalMemory;
 import static android.os.Process.killProcessQuiet;
 import static android.os.Process.startWebView;
-import static android.os.storage.StorageManager.PROP_ISOLATED_STORAGE;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
@@ -73,6 +72,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.text.TextUtils;
 import android.util.EventLog;
@@ -1246,10 +1246,8 @@
         long startTime = SystemClock.elapsedRealtime();
         if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
             checkSlow(startTime, "startProcess: removing from pids map");
-            synchronized (mService.mPidsSelfLocked) {
-                mService.mPidsSelfLocked.remove(app.pid);
-                mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-            }
+            mService.mPidsSelfLocked.remove(app.pid);
+            mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
             checkSlow(startTime, "startProcess: done removing from pids map");
             app.setPid(0);
         }
@@ -1281,8 +1279,7 @@
                     final IPackageManager pm = AppGlobals.getPackageManager();
                     permGids = pm.getPackageGids(app.info.packageName,
                             MATCH_DIRECT_BOOT_AUTO, app.userId);
-                    if (SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, false)
-                            && mountExtStorageFull) {
+                    if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
                         mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
                     } else {
                         StorageManagerInternal storageManagerInternal = LocalServices.getService(
@@ -1768,8 +1765,8 @@
             mService.cleanUpApplicationRecordLocked(oldApp, false, false, -1,
                     true /*replacingPid*/);
         }
+        mService.mPidsSelfLocked.put(pid, app);
         synchronized (mService.mPidsSelfLocked) {
-            mService.mPidsSelfLocked.put(pid, app);
             if (!procAttached) {
                 Message msg = mService.mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                 msg.obj = app;
@@ -1929,10 +1926,8 @@
                 .pendingStart)) {
             int pid = app.pid;
             if (pid > 0) {
-                synchronized (mService.mPidsSelfLocked) {
-                    mService.mPidsSelfLocked.remove(pid);
-                    mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
-                }
+                mService.mPidsSelfLocked.remove(pid);
+                mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                 mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
                 if (app.isolated) {
                     mService.mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index a5848ca..4c4a090 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -65,6 +65,7 @@
     // permission in the corresponding .te file your feature belongs to.
     @VisibleForTesting
     static final String[] sGlobalSettings = new String[] {
+            Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED,
     };
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index b817669..48b4145 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -1,17 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "CtsActivityManagerDeviceTestCases",
-      "options": [
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
       "name": "CtsActivityManagerDeviceSdk25TestCases",
       "options": [
         {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d27c9..6cde4ad 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -649,6 +649,9 @@
     private String mEnabledSurroundFormats;
     private boolean mSurroundModeChanged;
 
+    @GuardedBy("mSettingsLock")
+    private int mAssistantUid;
+
     // Intent "extra" data keys.
     public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
     public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1079,6 +1082,10 @@
             AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock);
             sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
             sendEnabledSurroundFormats(mContentResolver, true);
+            updateAssistantUId(true);
+        }
+        synchronized (mAccessibilityServiceUidsLock) {
+            AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
         }
         synchronized (mHdmiClientLock) {
             if (mHdmiManager != null && mHdmiTvClient != null) {
@@ -1404,6 +1411,39 @@
         }
     }
 
+    @GuardedBy("mSettingsLock")
+    private void updateAssistantUId(boolean forceUpdate) {
+        int assistantUid = 0;
+
+        // Consider assistants in the following order of priority:
+        // 1) voice interaction service
+        // 2) assistant
+        String assistantName = Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT);
+        if (TextUtils.isEmpty(assistantName)) {
+            assistantName = Settings.Secure.getStringForUser(
+                    mContentResolver,
+                    Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT);
+        }
+        if (!TextUtils.isEmpty(assistantName)) {
+            String packageName = ComponentName.unflattenFromString(assistantName).getPackageName();
+            if (!TextUtils.isEmpty(packageName)) {
+                try {
+                    assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.e(TAG,
+                            "updateAssistantUId() could not find UID for package: " + packageName);
+                }
+            }
+        }
+
+        if (assistantUid != mAssistantUid || forceUpdate) {
+            AudioSystem.setAssistantUid(assistantUid);
+            mAssistantUid = assistantUid;
+        }
+    }
+
     private void readPersistedSettings() {
         final ContentResolver cr = mContentResolver;
 
@@ -1447,6 +1487,7 @@
             readDockAudioSettings(cr);
             sendEncodedSurroundMode(cr, "readPersistedSettings");
             sendEnabledSurroundFormats(cr, true);
+            updateAssistantUId(true);
         }
 
         mMuteAffectedStreams = System.getIntForUser(cr,
@@ -5811,6 +5852,9 @@
                     mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS);
             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this);
+
+            mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
         }
 
         @Override
@@ -5832,6 +5876,7 @@
                 updateMasterMono(mContentResolver);
                 updateEncodedSurroundOutput();
                 sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
+                updateAssistantUId(false);
             }
         }
 
@@ -7658,6 +7703,7 @@
                         mAccessibilityServiceUids = uids.toArray();
                     }
                 }
+                AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
             }
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 2c2d404..eaa7a83 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -19,19 +19,11 @@
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.security.KeyStore;
-import android.text.TextUtils;
 import android.util.Slog;
 
-import com.android.internal.statusbar.IStatusBarService;
-
 import java.util.ArrayList;
 
 /**
@@ -39,88 +31,15 @@
  */
 public abstract class AuthenticationClient extends ClientMonitor {
     private long mOpId;
-    private Handler mHandler;
 
     public abstract int handleFailedAttempt();
     public abstract void resetFailedAttempts();
-    public abstract String getErrorString(int error, int vendorCode);
-    public abstract String getAcquiredString(int acquireInfo, int vendorCode);
-    /**
-      * @return one of {@link #TYPE_FINGERPRINT} {@link #TYPE_IRIS} or {@link #TYPE_FACE}
-      */
-    public abstract int getBiometricType();
 
     public static final int LOCKOUT_NONE = 0;
     public static final int LOCKOUT_TIMED = 1;
     public static final int LOCKOUT_PERMANENT = 2;
 
     private final boolean mRequireConfirmation;
-    // Callback mechanism received from the client
-    // (BiometricPrompt -> BiometricPromptService -> <Biometric>Service -> AuthenticationClient)
-    private IBiometricPromptReceiver mDialogReceiverFromClient;
-    private Bundle mBundle;
-    private IStatusBarService mStatusBarService;
-    private boolean mInLockout;
-    private TokenEscrow mEscrow;
-    protected boolean mDialogDismissed;
-
-    /**
-     * Container that holds the identifier and authToken. For biometrics that require user
-     * confirmation, these should not be sent to their final destinations until the user confirms.
-     */
-    class TokenEscrow {
-        final BiometricAuthenticator.Identifier mIdentifier;
-        final ArrayList<Byte> mToken;
-
-        TokenEscrow(BiometricAuthenticator.Identifier identifier, ArrayList<Byte> token) {
-            mIdentifier = identifier;
-            mToken = token;
-        }
-
-        BiometricAuthenticator.Identifier getIdentifier() {
-            return mIdentifier;
-        }
-
-        ArrayList<Byte> getToken() {
-            return mToken;
-        }
-    }
-
-    // Receives events from SystemUI and handles them before forwarding them to BiometricDialog
-    protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
-        @Override // binder call
-        public void onDialogDismissed(int reason) {
-            if (mBundle != null && mDialogReceiverFromClient != null) {
-                try {
-                    if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                        // Positive button is used by passive modalities as a "confirm" button,
-                        // do not send to client
-                        mDialogReceiverFromClient.onDialogDismissed(reason);
-                    }
-                    if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
-                        onError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
-                                0 /* vendorCode */);
-                    } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                        // Have the service send the token to KeyStore, and send onAuthenticated
-                        // to the application.
-                        if (mEscrow != null) {
-                            if (DEBUG) Slog.d(getLogTag(), "Confirmed");
-                            addTokenToKeyStore(mEscrow.getToken());
-                            notifyClientAuthenticationSucceeded(mEscrow.getIdentifier());
-                            mEscrow = null;
-                            onAuthenticationConfirmed();
-                        } else {
-                            Slog.e(getLogTag(), "Escrow is null!!!");
-                        }
-                    }
-                    mDialogDismissed = true;
-                } catch (RemoteException e) {
-                    Slog.e(getLogTag(), "Remote exception", e);
-                }
-                stop(true /* initiatedByClient */);
-            }
-        }
-    };
 
     /**
      * This method is called when authentication starts.
@@ -133,25 +52,13 @@
      */
     public abstract void onStop();
 
-    /**
-     * This method is called when biometric authentication was confirmed by the user. The client
-     * should be removed.
-     */
-    public abstract void onAuthenticationConfirmed();
-
     public AuthenticationClient(Context context, Metrics metrics,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
-            boolean restricted, String owner, Bundle bundle,
-            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
-            boolean requireConfirmation) {
+            boolean restricted, String owner, int cookie, boolean requireConfirmation) {
         super(context, metrics, daemon, halDeviceId, token, listener, targetUserId, groupId,
-                restricted, owner);
+                restricted, owner, cookie);
         mOpId = opId;
-        mBundle = bundle;
-        mDialogReceiverFromClient = dialogReceiver;
-        mStatusBarService = statusBarService;
-        mHandler = new Handler(Looper.getMainLooper());
         mRequireConfirmation = requireConfirmation;
     }
 
@@ -164,175 +71,99 @@
         stop(false /* initiatedByClient */);
     }
 
-    @Override
-    public boolean onAcquired(int acquiredInfo, int vendorCode) {
-        // If the dialog is showing, the client doesn't need to receive onAcquired messages.
-        if (mBundle != null) {
-            try {
-                if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-                    mStatusBarService.onBiometricHelp(getAcquiredString(acquiredInfo, vendorCode));
-                }
-                return false; // acquisition continues
-            } catch (RemoteException e) {
-                Slog.e(getLogTag(), "Remote exception when sending acquired message", e);
-                return true; // client failed
-            } finally {
-                // Good scans will keep the device awake
-                if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-                    notifyUserActivity();
-                }
-            }
-        } else {
-            return super.onAcquired(acquiredInfo, vendorCode);
-        }
-    }
-
-    @Override
-    public boolean onError(long deviceId, int error, int vendorCode) {
-        if (mDialogDismissed) {
-            // If user cancels authentication, the application has already received the
-            // ERROR_USER_CANCELED message from onDialogDismissed()
-            // and stopped the biometric hardware, so there is no need to send a
-            // ERROR_CANCELED message.
-            return true;
-        }
-        if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
-            try {
-                mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
-            } catch (RemoteException e) {
-                Slog.e(getLogTag(), "Remote exception when sending error", e);
-            }
-        }
-        return super.onError(deviceId, error, vendorCode);
-    }
-
-    public void setTitleIfEmpty(CharSequence title) {
-        if (TextUtils.isEmpty(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
-            mBundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
-        }
-    }
-
     public boolean isBiometricPrompt() {
-        return mBundle != null;
+        return getCookie() != 0;
     }
 
-    private void notifyClientAuthenticationSucceeded(BiometricAuthenticator.Identifier identifier)
-            throws RemoteException {
-        final BiometricServiceBase.ServiceListener listener = getListener();
-        // Explicitly have if/else here to make it super obvious in case the code is
-        // touched in the future.
-        if (!getIsRestricted()) {
-            listener.onAuthenticationSucceeded(
-                    getHalDeviceId(), identifier, getTargetUserId());
-        } else {
-            listener.onAuthenticationSucceeded(
-                    getHalDeviceId(), null, getTargetUserId());
-        }
-    }
-
-    private void addTokenToKeyStore(ArrayList<Byte> token) {
-        // Send the token to KeyStore
-        final byte[] byteToken = new byte[token.size()];
-        for (int i = 0; i < token.size(); i++) {
-            byteToken[i] = token.get(i);
-        }
-        KeyStore.getInstance().addAuthToken(byteToken);
+    public boolean getRequireConfirmation() {
+        return mRequireConfirmation;
     }
 
     @Override
     public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
-        if (authenticated) {
-            mAlreadyDone = true;
-            if (mRequireConfirmation) {
-                // Store the token so it can be sent to keystore after the user presses confirm
-                mEscrow = new TokenEscrow(identifier, token);
-            } else {
-                addTokenToKeyStore(token);
-            }
-        }
+        final BiometricServiceBase.ServiceListener listener = getListener();
 
+        mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
         boolean result = false;
 
-        // If the biometric dialog is showing, notify authentication succeeded
-        if (mBundle != null) {
-            try {
-                if (authenticated) {
-                    mStatusBarService.onBiometricAuthenticated();
-                } else {
-                    mStatusBarService.onBiometricHelp(getContext().getResources().getString(
-                            com.android.internal.R.string.biometric_not_recognized));
+        try {
+            if (authenticated) {
+                mAlreadyDone = true;
+                if (DEBUG) Slog.v(getLogTag(), "onAuthenticated(" + getOwnerString()
+                        + ", ID:" + identifier.getBiometricId()
+                        + ", isBP: " + isBiometricPrompt()
+                        + ", listener: " + listener
+                        + ", requireConfirmation: " + mRequireConfirmation);
+                if (listener != null) {
+                    vibrateSuccess();
                 }
-            } catch (RemoteException e) {
-                Slog.e(getLogTag(), "Failed to notify Authenticated:", e);
-            }
-        }
+                result = true;
+                resetFailedAttempts();
+                onStop();
 
-        final BiometricServiceBase.ServiceListener listener = getListener();
-        if (listener != null) {
-            try {
-                mMetricsLogger.action(mMetrics.actionBiometricAuth(), authenticated);
-                if (!authenticated) {
-                    listener.onAuthenticationFailed(getHalDeviceId());
-                } else {
-                    if (DEBUG) {
-                        Slog.v(getLogTag(), "onAuthenticated(owner=" + getOwnerString()
-                                + ", id=" + identifier.getBiometricId());
-                    }
-                    if (!mRequireConfirmation) {
-                        notifyClientAuthenticationSucceeded(identifier);
-                    }
+                final byte[] byteToken = new byte[token.size()];
+                for (int i = 0; i < token.size(); i++) {
+                    byteToken[i] = token.get(i);
                 }
-            } catch (RemoteException e) {
-                Slog.w(getLogTag(), "Failed to notify Authenticated:", e);
-                result = true; // client failed
-            }
-        } else {
-            result = true; // client not listening
-        }
-        if (!authenticated) {
-            if (listener != null) {
-                vibrateError();
-            }
-            // allow system-defined limit of number of attempts before giving up
-            int lockoutMode =  handleFailedAttempt();
-            if (lockoutMode != LOCKOUT_NONE) {
-                try {
-                    mInLockout = true;
-                    Slog.w(getLogTag(), "Forcing lockout (fp driver code should do this!), mode(" +
-                            lockoutMode + ")");
+                if (isBiometricPrompt() && listener != null) {
+                    // BiometricService will add the token to keystore
+                    listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
+                } else if (!isBiometricPrompt() && listener != null) {
+                    KeyStore.getInstance().addAuthToken(byteToken);
+                    try {
+                        // Explicitly have if/else here to make it super obvious in case the code is
+                        // touched in the future.
+                        if (!getIsRestricted()) {
+                            listener.onAuthenticationSucceeded(
+                                    getHalDeviceId(), identifier, getTargetUserId());
+                        } else {
+                            listener.onAuthenticationSucceeded(
+                                    getHalDeviceId(), null, getTargetUserId());
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(getLogTag(), "Remote exception", e);
+                    }
+                } else {
+                    // Client not listening
+                    Slog.w(getLogTag(), "Client not listening");
+                    result = true;
+                }
+            } else {
+                if (listener != null) {
+                    vibrateError();
+                }
+                // Allow system-defined limit of number of attempts before giving up
+                final int lockoutMode = handleFailedAttempt();
+                if (lockoutMode != LOCKOUT_NONE) {
+                    Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode("
+                            + lockoutMode + ")");
                     stop(false);
-                    int errorCode = lockoutMode == LOCKOUT_TIMED ?
-                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
-                            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-
-                    // Send the lockout message to the system dialog
-                    if (mBundle != null) {
-                        mStatusBarService.onBiometricError(
-                                getErrorString(errorCode, 0 /* vendorCode */));
-                        mHandler.postDelayed(() -> {
-                            try {
-                                listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
-                            } catch (RemoteException e) {
-                                Slog.w(getLogTag(), "RemoteException while sending error");
-                            }
-                        }, BiometricPrompt.HIDE_DIALOG_DELAY);
-                    } else {
-                        listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */);
+                    final int errorCode = lockoutMode == LOCKOUT_TIMED
+                            ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+                            : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+                    if (listener != null) {
+                        listener.onError(getHalDeviceId(), errorCode, 0 /* vendorCode */,
+                                getCookie());
                     }
-                } catch (RemoteException e) {
-                    Slog.w(getLogTag(), "Failed to notify lockout:", e);
+                } else {
+                    // Don't send onAuthenticationFailed if we're in lockout, it causes a
+                    // janky UI on Keyguard/BiometricPrompt since "authentication failed"
+                    // will show briefly and be replaced by "device locked out" message.
+                    if (listener != null) {
+                        if (isBiometricPrompt()) {
+                            listener.onAuthenticationFailedInternal(getCookie(),
+                                    getRequireConfirmation());
+                        } else {
+                            listener.onAuthenticationFailed(getHalDeviceId());
+                        }
+                    }
                 }
+                result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
             }
-            result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
-        } else {
-            if (listener != null) {
-                vibrateSuccess();
-            }
-            // we have a valid biometric that doesn't require confirmation, done
-            result |= !mRequireConfirmation;
-            resetFailedAttempts();
-            onStop();
+        } catch (RemoteException e) {
+            Slog.e(getLogTag(), "Remote exception", e);
+            result = true;
         }
         return result;
     }
@@ -353,16 +184,6 @@
                 return result;
             }
             if (DEBUG) Slog.w(getLogTag(), "client " + getOwnerString() + " is authenticating...");
-
-            // If authenticating with system dialog, show the dialog
-            if (mBundle != null) {
-                try {
-                    mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver,
-                            getBiometricType(), mRequireConfirmation, getTargetUserId());
-                } catch (RemoteException e) {
-                    Slog.e(getLogTag(), "Unable to show biometric dialog", e);
-                }
-            }
         } catch (RemoteException e) {
             Slog.e(getLogTag(), "startAuthentication failed", e);
             return ERROR_ESRCH;
@@ -390,18 +211,6 @@
         } catch (RemoteException e) {
             Slog.e(getLogTag(), "stopAuthentication failed", e);
             return ERROR_ESRCH;
-        } finally {
-            // If the user already cancelled authentication (via some interaction with the
-            // dialog, we do not need to hide it since it's already hidden.
-            // If the device is in lockout, don't hide the dialog - it will automatically hide
-            // after BiometricPrompt.HIDE_DIALOG_DELAY
-            if (mBundle != null && !mDialogDismissed && !mInLockout) {
-                try {
-                    mStatusBarService.hideBiometricDialog();
-                } catch (RemoteException e) {
-                    Slog.e(getLogTag(), "Unable to hide biometric dialog", e);
-                }
-            }
         }
 
         mAlreadyCancelled = true;
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 5f09189..add55ea 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -19,9 +19,17 @@
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
+import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
 import android.app.UserSwitchObserver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -32,9 +40,9 @@
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
-import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.face.FaceManager;
 import android.hardware.face.IFaceService;
 import android.hardware.fingerprint.FingerprintManager;
@@ -50,14 +58,21 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.security.KeyStore;
+import android.text.TextUtils;
 import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Random;
 
 /**
  * System service that arbitrates the modality for BiometricPrompt to use.
@@ -66,32 +81,10 @@
 
     private static final String TAG = "BiometricService";
 
-    /**
-     * No biometric methods or nothing has been enrolled.
-     * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
-     * modalities when calling authenticate().
-     */
-    private static final int BIOMETRIC_NONE = 0;
-
-    /**
-     * Constant representing fingerprint.
-     */
-    private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
-
-    /**
-     * Constant representing iris.
-     */
-    private static final int BIOMETRIC_IRIS = 1 << 1;
-
-    /**
-     * Constant representing face.
-     */
-    private static final int BIOMETRIC_FACE = 1 << 2;
-
     private static final int[] FEATURE_ID = {
-            BIOMETRIC_FINGERPRINT,
-            BIOMETRIC_IRIS,
-            BIOMETRIC_FACE
+        TYPE_FINGERPRINT,
+        TYPE_IRIS,
+        TYPE_FACE
     };
 
     private final AppOpsManager mAppOps;
@@ -242,10 +235,367 @@
      */
     private final class BiometricServiceWrapper extends IBiometricService.Stub {
 
+        /**
+         * Authentication either just called and we have not transitioned to the CALLED state, or
+         * authentication terminated (success or error).
+         */
+        private static final int STATE_AUTH_IDLE = 0;
+        /**
+         * Authentication was called and we are waiting for the <Biometric>Services to return their
+         * cookies before starting the hardware and showing the BiometricPrompt.
+         */
+        private static final int STATE_AUTH_CALLED = 1;
+        /**
+         * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
+         */
+        private static final int STATE_AUTH_STARTED = 2;
+        /**
+         * Authentication is paused, waiting for the user to press "try again" button. Since the
+         * try again button requires us to cancel authentication, this represents the state where
+         * ERROR_CANCELED is not received yet.
+         */
+        private static final int STATE_AUTH_PAUSED = 3;
+        /**
+         * Same as above, except the ERROR_CANCELED has been received.
+         */
+        private static final int STATE_AUTH_PAUSED_CANCELED = 4;
+        /**
+         * Authentication is successful, but we're waiting for the user to press "confirm" button.
+         */
+        private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+
+        final class AuthSession {
+            // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
+            // <Biometric>Services before we can start authenticating. Pairs that have been returned
+            // are moved to mModalitiesMatched.
+            final HashMap<Integer, Integer> mModalitiesWaiting;
+            // Pairs that have been matched.
+            final HashMap<Integer, Integer> mModalitiesMatched = new HashMap<>();
+
+            // The following variables are passed to authenticateInternal, which initiates the
+            // appropriate <Biometric>Services.
+            final IBinder mToken;
+            final long mSessionId;
+            final int mUserId;
+            // Original receiver from BiometricPrompt.
+            final IBiometricServiceReceiver mClientReceiver;
+            final String mOpPackageName;
+            // Info to be shown on BiometricDialog when all cookies are returned.
+            final Bundle mBundle;
+            final int mCallingUid;
+            final int mCallingPid;
+            final int mCallingUserId;
+            // Continue authentication with the same modality/modalities after "try again" is
+            // pressed
+            final int mModality;
+
+            // The current state, which can be either idle, called, or started
+            private int mState = STATE_AUTH_IDLE;
+            // For explicit confirmation, do not send to keystore until the user has confirmed
+            // the authentication.
+            byte[] mTokenEscrow;
+
+            AuthSession(HashMap<Integer, Integer> modalities, IBinder token, long sessionId,
+                    int userId, IBiometricServiceReceiver receiver, String opPackageName,
+                    Bundle bundle, int callingUid, int callingPid, int callingUserId,
+                    int modality) {
+                mModalitiesWaiting = modalities;
+                mToken = token;
+                mSessionId = sessionId;
+                mUserId = userId;
+                mClientReceiver = receiver;
+                mOpPackageName = opPackageName;
+                mBundle = bundle;
+                mCallingUid = callingUid;
+                mCallingPid = callingPid;
+                mCallingUserId = callingUserId;
+                mModality = modality;
+            }
+
+            boolean containsCookie(int cookie) {
+                if (mModalitiesWaiting != null && mModalitiesWaiting.containsValue(cookie)) {
+                    return true;
+                }
+                if (mModalitiesMatched != null && mModalitiesMatched.containsValue(cookie)) {
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        final class BiometricTaskStackListener extends TaskStackListener {
+            @Override
+            public void onTaskStackChanged() {
+                try {
+                    final List<ActivityManager.RunningTaskInfo> runningTasks =
+                            mActivityTaskManager.getTasks(1);
+                    if (!runningTasks.isEmpty()) {
+                        final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                        if (mCurrentAuthSession != null
+                                && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)
+                                && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+                            // We only care about this state, since <Biometric>Service will
+                            // cancel any client that's still in STATE_AUTH_STARTED
+                            mStatusBarService.hideBiometricDialog();
+                            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+                            mCurrentAuthSession.mClientReceiver.onError(
+                                    BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                                    getContext().getString(
+                                            com.android.internal.R.string.biometric_error_canceled)
+                            );
+                            mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                            mCurrentAuthSession = null;
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to get running tasks", e);
+                }
+            }
+        }
+
+        private final IActivityTaskManager mActivityTaskManager = getContext().getSystemService(
+                ActivityTaskManager.class).getService();
+        private final IStatusBarService mStatusBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        private final BiometricTaskStackListener mTaskStackListener =
+                new BiometricTaskStackListener();
+        private final Random mRandom = new Random();
+
+        // The current authentication session, null if idle/done. We need to track both the current
+        // and pending sessions since errors may be sent to either.
+        private AuthSession mCurrentAuthSession;
+        private AuthSession mPendingAuthSession;
+
+        // Wrap the client's receiver so we can do things with the BiometricDialog first
+        private final IBiometricServiceReceiverInternal mInternalReceiver =
+                new IBiometricServiceReceiverInternal.Stub() {
+            @Override
+            public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
+                    throws RemoteException {
+                try {
+                    if (!requireConfirmation) {
+                        mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+                        KeyStore.getInstance().addAuthToken(token);
+                        mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                        mCurrentAuthSession = null;
+                    } else {
+                        // Store the auth token and submit it to keystore after the confirmation
+                        // button has been pressed.
+                        mCurrentAuthSession.mTokenEscrow = token;
+                        mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
+                    }
+
+                    // Notify SysUI that the biometric has been authenticated. SysUI already knows
+                    // the implicit/explicit state and will react accordingly.
+                    mStatusBarService.onBiometricAuthenticated();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception", e);
+                }
+            }
+
+            @Override
+            public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+                    throws RemoteException {
+                try {
+                    mStatusBarService.onBiometricHelp(getContext().getResources().getString(
+                            com.android.internal.R.string.biometric_not_recognized));
+                    if (requireConfirmation) {
+                        mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+                        mStatusBarService.showBiometricTryAgain();
+                        // Cancel authentication. Skip the token/package check since we are
+                        // cancelling from system server. The interface is permission protected so
+                        // this is fine.
+                        cancelInternal(null /* token */, null /* package */,
+                                false /* fromClient */);
+                    }
+                    mCurrentAuthSession.mClientReceiver.onAuthenticationFailed();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception", e);
+                }
+            }
+
+            @Override
+            public void onError(int cookie, int error, String message) throws RemoteException {
+                Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+                // Errors can either be from the current auth session or the pending auth session.
+                // The pending auth session may receive errors such as ERROR_LOCKOUT before
+                // it becomes the current auth session. Similarly, the current auth session may
+                // receive errors such as ERROR_CANCELED while the pending auth session is preparing
+                // to be started. Thus we must match error messages with their cookies to be sure
+                // of their intended receivers.
+                try {
+                    if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+                        if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
+                            mStatusBarService.onBiometricError(message);
+                            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+                            if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+                                    mCurrentAuthSession.mClientReceiver.onError(error, message);
+                                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                                    mCurrentAuthSession = null;
+                                    mStatusBarService.hideBiometricDialog();
+                            } else {
+                                // Send errors after the dialog is dismissed.
+                                mHandler.postDelayed(() -> {
+                                    try {
+                                        mCurrentAuthSession.mClientReceiver.onError(error, message);
+                                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                                        mCurrentAuthSession = null;
+                                    } catch (RemoteException e) {
+                                        Slog.e(TAG, "Remote exception", e);
+                                    }
+                                }, BiometricPrompt.HIDE_DIALOG_DELAY);
+                            }
+                        } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+                                || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) {
+                            if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED
+                                    && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
+                                // Skip the first ERROR_CANCELED message when this happens, since
+                                // "try again" requires us to cancel authentication but keep
+                                // the prompt showing.
+                                mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED;
+                            } else {
+                                // In the "try again" state, we should forward canceled errors to
+                                // the client and and clean up.
+                                mCurrentAuthSession.mClientReceiver.onError(error, message);
+                                mStatusBarService.onBiometricError(message);
+                                mActivityTaskManager.unregisterTaskStackListener(
+                                        mTaskStackListener);
+                                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                                mCurrentAuthSession = null;
+                            }
+                        } else {
+                            Slog.e(TAG, "Impossible session error state: "
+                                    + mCurrentAuthSession.mState);
+                        }
+                    } else if (mPendingAuthSession != null
+                            && mPendingAuthSession.containsCookie(cookie)) {
+                        if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
+                            mPendingAuthSession.mClientReceiver.onError(error, message);
+                            mPendingAuthSession.mState = STATE_AUTH_IDLE;
+                            mPendingAuthSession = null;
+                        } else {
+                            Slog.e(TAG, "Impossible pending session error state: "
+                                    + mPendingAuthSession.mState);
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception", e);
+                }
+            }
+
+            @Override
+            public void onAcquired(int acquiredInfo, String message) throws RemoteException {
+                if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
+                    try {
+                        mStatusBarService.onBiometricHelp(message);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Remote exception", e);
+                    }
+                }
+            }
+
+            @Override
+            public void onDialogDismissed(int reason) throws RemoteException {
+                if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+                    // Positive button is used by passive modalities as a "confirm" button,
+                    // do not send to client
+                    mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+                    // Cancel authentication. Skip the token/package check since we are cancelling
+                    // from system server. The interface is permission protected so this is fine.
+                    cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+                }
+                if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
+                    mCurrentAuthSession.mClientReceiver.onError(
+                            BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+                            getContext().getString(
+                                    com.android.internal.R.string.biometric_error_user_canceled));
+                } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+                    // Have the service send the token to KeyStore, and send onAuthenticated
+                    // to the application
+                    KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
+                    mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+                }
+                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                mCurrentAuthSession = null;
+            }
+
+            @Override
+            public void onTryAgainPressed() {
+                Slog.d(TAG, "onTryAgainPressed");
+                // No need to check permission, since it can only be invoked by SystemUI
+                // (or system server itself).
+                mHandler.post(() -> {
+                    authenticateInternal(mCurrentAuthSession.mToken,
+                            mCurrentAuthSession.mSessionId,
+                            mCurrentAuthSession.mUserId,
+                            mCurrentAuthSession.mClientReceiver,
+                            mCurrentAuthSession.mOpPackageName,
+                            mCurrentAuthSession.mBundle,
+                            mCurrentAuthSession.mCallingUid,
+                            mCurrentAuthSession.mCallingPid,
+                            mCurrentAuthSession.mCallingUserId,
+                            mCurrentAuthSession.mModality);
+                });
+            }
+        };
+
+        @Override // Binder call
+        public void onReadyForAuthentication(int cookie, boolean requireConfirmation, int userId) {
+            checkInternalPermission();
+
+            Iterator it = mPendingAuthSession.mModalitiesWaiting.entrySet().iterator();
+            while (it.hasNext()) {
+                Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+                if (pair.getValue() == cookie) {
+                    mPendingAuthSession.mModalitiesMatched.put(pair.getKey(), pair.getValue());
+                    mPendingAuthSession.mModalitiesWaiting.remove(pair.getKey());
+                    Slog.d(TAG, "Matched cookie: " + cookie + ", "
+                            + mPendingAuthSession.mModalitiesWaiting.size() + " remaining");
+                    break;
+                }
+            }
+
+            if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) {
+                final boolean mContinuing = mCurrentAuthSession != null
+                        && mCurrentAuthSession.mState == STATE_AUTH_PAUSED;
+                mCurrentAuthSession = mPendingAuthSession;
+                mPendingAuthSession = null;
+
+                mCurrentAuthSession.mState = STATE_AUTH_STARTED;
+                try {
+                    int modality = TYPE_NONE;
+                    it = mCurrentAuthSession.mModalitiesMatched.entrySet().iterator();
+                    while (it.hasNext()) {
+                        Map.Entry<Integer, Integer> pair = (Map.Entry) it.next();
+                        if (pair.getKey() == TYPE_FINGERPRINT) {
+                            mFingerprintService.startPreparedClient(pair.getValue());
+                        } else if (pair.getKey() == TYPE_IRIS) {
+                            Slog.e(TAG, "Iris unsupported");
+                        } else if (pair.getKey() == TYPE_FACE) {
+                            mFaceService.startPreparedClient(pair.getValue());
+                        } else {
+                            Slog.e(TAG, "Unknown modality: " + pair.getKey());
+                        }
+                        modality |= pair.getKey();
+                    }
+
+                    if (!mContinuing) {
+                        mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
+                                mInternalReceiver, modality, requireConfirmation, userId);
+                        mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception", e);
+                }
+            }
+        }
+
         @Override // Binder call
         public void authenticate(IBinder token, long sessionId, int userId,
-                IBiometricServiceReceiver receiver, int flags, String opPackageName,
-                Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+                IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle)
+                throws RemoteException {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -261,16 +611,45 @@
                 checkInternalPermission();
             }
 
-            if (token == null || receiver == null || opPackageName == null || bundle == null
-                    || dialogReceiver == null) {
+            if (token == null || receiver == null || opPackageName == null || bundle == null) {
                 Slog.e(TAG, "Unable to authenticate, one or more null arguments");
                 return;
             }
 
             // Check the usage of this in system server. Need to remove this check if it becomes
             // a public API.
-            if (bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false)) {
+            final boolean useDefaultTitle =
+                    bundle.getBoolean(BiometricPrompt.KEY_USE_DEFAULT_TITLE, false);
+            if (useDefaultTitle) {
                 checkInternalPermission();
+                // Set the default title if necessary
+                try {
+                    if (useDefaultTitle) {
+                        final List<ActivityManager.RunningAppProcessInfo> procs =
+                                ActivityManager.getService().getRunningAppProcesses();
+                        for (int i = 0; i < procs.size(); i++) {
+                            final ActivityManager.RunningAppProcessInfo info = procs.get(i);
+                            if (info.uid == callingUid
+                                    && info.importance == IMPORTANCE_FOREGROUND) {
+                                PackageManager pm = getContext().getPackageManager();
+                                final CharSequence label = pm.getApplicationLabel(
+                                        pm.getApplicationInfo(info.processName,
+                                                PackageManager.GET_META_DATA));
+                                final String title = getContext()
+                                        .getString(R.string.biometric_dialog_default_title, label);
+                                if (TextUtils.isEmpty(
+                                        bundle.getCharSequence(BiometricPrompt.KEY_TITLE))) {
+                                    bundle.putCharSequence(BiometricPrompt.KEY_TITLE, title);
+                                }
+                                break;
+                            }
+                        }
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception", e);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Slog.e(TAG, "Name not found", e);
+                }
             }
 
             mHandler.post(() -> {
@@ -285,13 +664,13 @@
                                 getContext().getString(R.string.biometric_error_hw_unavailable);
                         switch (error) {
                             case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
-                                receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+                                receiver.onError(error, hardwareUnavailable);
                                 break;
                             case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
-                                receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+                                receiver.onError(error, hardwareUnavailable);
                                 break;
                             case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
-                                receiver.onError(0 /* deviceId */, error,
+                                receiver.onError(error,
                                         getErrorString(modality, error, 0 /* vendorCode */));
                                 break;
                             default:
@@ -304,60 +683,91 @@
                     return;
                 }
 
-                // Actually start authentication
                 mCurrentModality = modality;
-                try {
-                    // No polymorphism :(
-                    if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
-                        mFingerprintService.authenticateFromService(token, sessionId, userId,
-                                receiver, flags, opPackageName, bundle, dialogReceiver,
-                                callingUid, callingPid, callingUserId);
-                    } else if (mCurrentModality == BIOMETRIC_IRIS) {
-                        Slog.w(TAG, "Unsupported modality");
-                    } else if (mCurrentModality == BIOMETRIC_FACE) {
-                        mFaceService.authenticateFromService(true /* requireConfirmation */,
-                                token, sessionId, userId, receiver, flags, opPackageName,
-                                bundle, dialogReceiver, callingUid, callingPid, callingUserId);
-                    } else {
-                        Slog.w(TAG, "Unsupported modality");
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to start authentication", e);
-                }
+
+                // Actually start authentication
+                authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
+                        callingUid, callingPid, callingUserId, modality);
             });
         }
 
+        /**
+         * authenticate() (above) which is called from BiometricPrompt determines which
+         * modality/modalities to start authenticating with. authenticateInternal() should only be
+         * used for:
+         * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
+         *    invoked, shortly after which BiometricPrompt is shown and authentication starts
+         * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
+         *    and the user has pressed "try again"
+         */
+        private void authenticateInternal(IBinder token, long sessionId, int userId,
+                IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
+                int callingUid, int callingPid, int callingUserId, int modality) {
+            try {
+                // Generate random cookies to pass to the services that should prepare to start
+                // authenticating. Store the cookie here and wait for all services to "ack"
+                // with the cookie. Once all cookies are received, we can show the prompt
+                // and let the services start authenticating. The cookie should be non-zero.
+                final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
+                Slog.d(TAG, "Creating auth session. Modality: " + modality
+                        + ", cookie: " + cookie);
+                final HashMap<Integer, Integer> authenticators = new HashMap<>();
+                authenticators.put(modality, cookie);
+                mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
+                        receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
+                        modality);
+                mPendingAuthSession.mState = STATE_AUTH_CALLED;
+                // No polymorphism :(
+                if ((modality & TYPE_FINGERPRINT) != 0) {
+                    mFingerprintService.prepareForAuthentication(token, sessionId, userId,
+                            mInternalReceiver, opPackageName, cookie,
+                            callingUid, callingPid, callingUserId);
+                }
+                if ((modality & TYPE_IRIS) != 0) {
+                    Slog.w(TAG, "Iris unsupported");
+                }
+                if ((modality & TYPE_FACE) != 0) {
+                    mFaceService.prepareForAuthentication(true /* requireConfirmation */,
+                            token, sessionId, userId, mInternalReceiver, opPackageName,
+                            cookie, callingUid, callingPid, callingUserId);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to start authentication", e);
+            }
+        }
+
         @Override // Binder call
         public void cancelAuthentication(IBinder token, String opPackageName)
                 throws RemoteException {
             checkPermission();
-
             if (token == null || opPackageName == null) {
                 Slog.e(TAG, "Unable to cancel, one or more null arguments");
                 return;
             }
 
-            final int callingUid = Binder.getCallingUid();
-            final int callingPid = Binder.getCallingPid();
-            final int callingUserId = UserHandle.getCallingUserId();
+            // We need to check the current authenticators state. If we're pending confirm
+            // or idle, we need to dismiss the dialog and send an ERROR_CANCELED to the client,
+            // since we won't be getting an onError from the driver.
+            if (mCurrentAuthSession != null && mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+                mHandler.post(() -> {
+                    try {
+                        // Send error to client
+                        mCurrentAuthSession.mClientReceiver.onError(
+                                BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                                getContext().getString(
+                                        com.android.internal.R.string.biometric_error_user_canceled)
+                        );
 
-            mHandler.post(() -> {
-                try {
-                    if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
-                        mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
-                                callingUid, callingPid, callingUserId);
-                    } else if (mCurrentModality == BIOMETRIC_IRIS) {
-                        Slog.w(TAG, "Unsupported modality");
-                    } else if (mCurrentModality == BIOMETRIC_FACE) {
-                        mFaceService.cancelAuthenticationFromService(token, opPackageName,
-                                callingUid, callingPid, callingUserId);
-                    } else {
-                        Slog.w(TAG, "Unsupported modality");
+                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                        mCurrentAuthSession = null;
+                        mStatusBarService.hideBiometricDialog();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Remote exception", e);
                     }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to cancel authentication");
-                }
-            });
+                });
+            } else {
+                cancelInternal(token, opPackageName, true /* fromClient */);
+            }
         }
 
         @Override // Binder call
@@ -402,6 +812,31 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
+
+        void cancelInternal(IBinder token, String opPackageName, boolean fromClient) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int callingUserId = UserHandle.getCallingUserId();
+            mHandler.post(() -> {
+                try {
+                    // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+                    // drivers have canceled authentication.
+                    if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
+                        mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+                                callingUid, callingPid, callingUserId, fromClient);
+                    }
+                    if ((mCurrentModality & TYPE_IRIS) != 0) {
+                        Slog.w(TAG, "Iris unsupported");
+                    }
+                    if ((mCurrentModality & TYPE_FACE) != 0) {
+                        mFaceService.cancelAuthenticationFromService(token, opPackageName,
+                                callingUid, callingPid, callingUserId, fromClient);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Unable to cancel authentication");
+                }
+            });
+        }
     }
 
     private void checkAppOp(String opPackageName, int callingUid) {
@@ -413,7 +848,7 @@
     }
 
     private void checkInternalPermission() {
-        getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+        getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
                 "Must have USE_BIOMETRIC_INTERNAL permission");
     }
 
@@ -490,16 +925,19 @@
      * returns errors through the callback (no biometric feature, hardware not detected, no
      * templates enrolled, etc). This service must not start authentication if errors are sent.
      *
-     * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
-     * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+     * @Returns A pair [Modality, Error] with Modality being one of
+     * {@link BiometricAuthenticator#TYPE_NONE},
+     * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
+     * {@link BiometricAuthenticator#TYPE_IRIS},
+     * {@link BiometricAuthenticator#TYPE_FACE}
      * and the error containing one of the {@link BiometricConstants} errors.
      */
     private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
-        int modality = BIOMETRIC_NONE;
+        int modality = TYPE_NONE;
 
         // No biometric features, send error
         if (mAuthenticators.isEmpty()) {
-            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
+            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
         }
 
         // Assuming that authenticators are listed in priority-order, the rest of this function
@@ -512,13 +950,13 @@
         boolean hasTemplatesEnrolled = false;
         boolean enabledForApps = false;
 
-        int firstHwAvailable = BIOMETRIC_NONE;
+        int firstHwAvailable = TYPE_NONE;
         for (int i = 0; i < mAuthenticators.size(); i++) {
             modality = mAuthenticators.get(i).getType();
             BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
             if (authenticator.isHardwareDetected()) {
                 isHardwareDetected = true;
-                if (firstHwAvailable == BIOMETRIC_NONE) {
+                if (firstHwAvailable == TYPE_NONE) {
                     // Store the first one since we want to return the error in correct priority
                     // order.
                     firstHwAvailable = modality;
@@ -538,13 +976,13 @@
 
         // Check error conditions
         if (!isHardwareDetected) {
-            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
         } else if (!hasTemplatesEnrolled) {
             // Return the modality here so the correct error string can be sent. This error is
             // preferred over !enabledForApps
             return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
         } else if (!enabledForApps) {
-            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
         }
 
         return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
@@ -552,11 +990,11 @@
 
     private boolean isEnabledForApp(int modality) {
         switch(modality) {
-            case BIOMETRIC_FINGERPRINT:
+            case TYPE_FINGERPRINT:
                 return true;
-            case BIOMETRIC_IRIS:
+            case TYPE_IRIS:
                 return true;
-            case BIOMETRIC_FACE:
+            case TYPE_FACE:
                 return mSettingObserver.getFaceEnabledForApps();
             default:
                 Slog.w(TAG, "Unsupported modality: " + modality);
@@ -566,12 +1004,12 @@
 
     private String getErrorString(int type, int error, int vendorCode) {
         switch (type) {
-            case BIOMETRIC_FINGERPRINT:
+            case TYPE_FINGERPRINT:
                 return FingerprintManager.getErrorString(getContext(), error, vendorCode);
-            case BIOMETRIC_IRIS:
+            case TYPE_IRIS:
                 Slog.w(TAG, "Modality not supported");
                 return null; // not supported
-            case BIOMETRIC_FACE:
+            case TYPE_FACE:
                 return FaceManager.getErrorString(getContext(), error, vendorCode);
             default:
                 Slog.w(TAG, "Unable to get error string for modality: " + type);
@@ -581,12 +1019,12 @@
 
     private BiometricAuthenticator getAuthenticator(int type) {
         switch (type) {
-            case BIOMETRIC_FINGERPRINT:
+            case TYPE_FINGERPRINT:
                 return (FingerprintManager)
                         getContext().getSystemService(Context.FINGERPRINT_SERVICE);
-            case BIOMETRIC_IRIS:
+            case TYPE_IRIS:
                 return null;
-            case BIOMETRIC_FACE:
+            case TYPE_FACE:
                 return (FaceManager)
                         getContext().getSystemService(Context.FACE_SERVICE);
             default:
@@ -596,11 +1034,11 @@
 
     private boolean hasFeature(int type) {
         switch (type) {
-            case BIOMETRIC_FINGERPRINT:
+            case TYPE_FINGERPRINT:
                 return mHasFeatureFingerprint;
-            case BIOMETRIC_IRIS:
+            case TYPE_IRIS:
                 return mHasFeatureIris;
-            case BIOMETRIC_FACE:
+            case TYPE_FACE:
                 return mHasFeatureFace;
             default:
                 return false;
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 74d742a..9649ccd 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -16,7 +16,6 @@
 
 package com.android.server.biometrics;
 
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
 
 import android.app.ActivityManager;
@@ -36,8 +35,9 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.fingerprint.Fingerprint;
 import android.os.Binder;
 import android.os.Bundle;
@@ -56,7 +56,6 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
-import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
@@ -106,6 +105,7 @@
     protected final AppOpsManager mAppOps;
     protected final H mHandler = new H();
 
+    private IBiometricService mBiometricService;
     private ClientMonitor mCurrentClient;
     private ClientMonitor mPendingClient;
     private PerformanceStats mPerformanceStats;
@@ -223,12 +223,9 @@
 
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, Bundle bundle,
-                IBiometricPromptReceiver dialogReceiver,
-                IStatusBarService statusBarService, boolean requireConfirmation) {
-            super(context, getMetrics(), daemon, halDeviceId, token, listener,
-                    targetUserId, groupId, opId, restricted, owner, bundle, dialogReceiver,
-                    statusBarService, requireConfirmation);
+                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+            super(context, getMetrics(), daemon, halDeviceId, token, listener, targetUserId,
+                    groupId, opId, restricted, owner, cookie, requireConfirmation);
         }
 
         @Override
@@ -279,11 +276,6 @@
             }
             return AuthenticationClient.LOCKOUT_NONE;
         }
-
-        @Override
-        public void onAuthenticationConfirmed() {
-            removeClient(mCurrentClient);
-        }
     }
 
     protected class EnrollClientImpl extends EnrollClient {
@@ -345,18 +337,28 @@
         default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
                 int remaining) throws RemoteException {};
 
-        void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
-                throws RemoteException;
+        void onAcquired(long deviceId, int acquiredInfo, int vendorCode) throws RemoteException;
 
-        void onAuthenticationSucceeded(long deviceId,
-                BiometricAuthenticator.Identifier biometric, int userId)
-                throws RemoteException;
+        default void onAuthenticationSucceeded(long deviceId,
+                BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+            throw new UnsupportedOperationException("Stub!");
+        }
 
-        void onAuthenticationFailed(long deviceId)
-                throws RemoteException;
+        default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+                throws RemoteException {
+            throw new UnsupportedOperationException("Stub!");
+        }
 
-        void onError(long deviceId, int error, int vendorCode)
-                throws RemoteException;
+        default void onAuthenticationFailed(long deviceId) throws RemoteException {
+            throw new UnsupportedOperationException("Stub!");
+        }
+
+        default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+                throws RemoteException {
+            throw new UnsupportedOperationException("Stub!");
+        }
+
+        void onError(long deviceId, int error, int vendorCode, int cookie) throws RemoteException;
 
         default void onRemoved(BiometricAuthenticator.Identifier identifier,
                 int remaining) throws RemoteException {};
@@ -366,6 +368,37 @@
     }
 
     /**
+     * Wraps the callback interface from Service -> BiometricPrompt
+     */
+    protected abstract class BiometricServiceListener implements ServiceListener {
+        private IBiometricServiceReceiverInternal mWrapperReceiver;
+
+        public BiometricServiceListener(IBiometricServiceReceiverInternal wrapperReceiver) {
+            mWrapperReceiver = wrapperReceiver;
+        }
+
+        public IBiometricServiceReceiverInternal getWrapperReceiver() {
+            return mWrapperReceiver;
+        }
+
+        @Override
+        public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
+                throws RemoteException {
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
+            }
+        }
+
+        @Override
+        public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+                throws RemoteException {
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+            }
+        }
+    }
+
+    /**
      * Wraps a portion of the interface from Service -> Daemon that is used by the ClientMonitor
      * subclasses.
      */
@@ -706,30 +739,6 @@
         }
 
         mHandler.post(() -> {
-            if (client.isBiometricPrompt()) {
-                try {
-                    final List<ActivityManager.RunningAppProcessInfo> procs =
-                            ActivityManager.getService().getRunningAppProcesses();
-                    for (int i = 0; i < procs.size(); i++) {
-                        final ActivityManager.RunningAppProcessInfo info = procs.get(i);
-                        if (info.uid == callingUid && info.importance == IMPORTANCE_FOREGROUND) {
-                            PackageManager pm = getContext().getPackageManager();
-                            final CharSequence label = pm.getApplicationLabel(
-                                    pm.getApplicationInfo(info.processName,
-                                            PackageManager.GET_META_DATA));
-                            final String title = getContext()
-                                    .getString(R.string.biometric_dialog_default_title, label);
-                            client.setTitleIfEmpty(title);
-                            break;
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Slog.e(getTag(), "Unable to get application name", e);
-                } catch (PackageManager.NameNotFoundException e) {
-                    Slog.e(getTag(), "Unable to get application name", e);
-                }
-            }
-
             mMetricsLogger.histogram(getMetrics().tagAuthToken(), opId != 0L ? 1 : 0);
 
             // Get performance stats object for this user.
@@ -751,29 +760,37 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         final int callingUserId = UserHandle.getCallingUserId();
-        cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+        cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId,
+                true /* fromClient */);
     }
 
     protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
-            int callingUid, int callingPid, int callingUserId) {
-        if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
-                callingUserId)) {
-            if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
-            return;
+            int callingUid, int callingPid, int callingUserId, boolean fromClient) {
+        if (fromClient) {
+            // Only check this if cancel was called from the client (app). If cancel was called
+            // from BiometricService, it means the dialog was dismissed due to user interaction.
+            if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
+                    callingUserId)) {
+                if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
+                return;
+            }
         }
 
         mHandler.post(() -> {
             ClientMonitor client = mCurrentClient;
             if (client instanceof AuthenticationClient) {
-                if (client.getToken() == token) {
-                    if (DEBUG) Slog.v(getTag(), "stop client " + client.getOwnerString());
+                if (client.getToken() == token || !fromClient) {
+                    if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
+                            + ", fromClient: " + fromClient);
+                    // If cancel was from BiometricService, it means the dialog was dismissed
+                    // and authentication should be canceled.
                     client.stop(client.getToken() == token);
                 } else {
-                    if (DEBUG) Slog.v(getTag(), "can't stop client "
-                            + client.getOwnerString() + " since tokens don't match");
+                    if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
+                            + " since tokens don't match. fromClient: " + fromClient);
                 }
             } else if (client != null) {
-                if (DEBUG) Slog.v(getTag(), "can't cancel non-authenticating client "
+                if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
                         + client.getOwnerString());
             }
         });
@@ -805,8 +822,7 @@
 
         int lockoutMode = getLockoutMode();
         if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) {
-            Slog.v(getTag(), "In lockout mode(" + lockoutMode +
-                    ") ; disallowing authentication");
+            Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
             int errorCode = lockoutMode == AuthenticationClient.LOCKOUT_TIMED ?
                     BiometricConstants.BIOMETRIC_ERROR_LOCKOUT :
                     BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
@@ -919,7 +935,6 @@
         if (currentClient != null) {
             if (DEBUG) Slog.v(getTag(), "request stop current client " +
                     currentClient.getOwnerString());
-
             // This check only matters for FingerprintService, since enumerate may call back
             // multiple times.
             if (currentClient instanceof FingerprintService.EnumerateClientImpl ||
@@ -940,17 +955,51 @@
             mHandler.removeCallbacks(mResetClientState);
             mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
         } else if (newClient != null) {
-            mCurrentClient = newClient;
-            if (DEBUG) Slog.v(getTag(), "starting client "
-                    + newClient.getClass().getSuperclass().getSimpleName()
-                    + "(" + newClient.getOwnerString() + ")"
-                    + ", initiatedByClient = " + initiatedByClient);
-            notifyClientActiveCallbacks(true);
+            // For BiometricPrompt clients, do not start until
+            // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
+            // modalities are ready before initiating authentication.
+            if (newClient instanceof AuthenticationClient) {
+                AuthenticationClient client = (AuthenticationClient) newClient;
+                if (client.isBiometricPrompt()) {
+                    if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
+                    mCurrentClient = newClient;
+                    if (mBiometricService == null) {
+                        mBiometricService = IBiometricService.Stub.asInterface(
+                                ServiceManager.getService(Context.BIOMETRIC_SERVICE));
+                    }
+                    try {
+                        mBiometricService.onReadyForAuthentication(client.getCookie(),
+                                client.getRequireConfirmation(), client.getTargetUserId());
+                    } catch (RemoteException e) {
+                        Slog.e(getTag(), "Remote exception", e);
+                    }
+                    return;
+                }
+            }
 
-            newClient.start();
+            // We are not a BiometricPrompt client, start the client immediately
+            mCurrentClient = newClient;
+            startCurrentClient(mCurrentClient.getCookie());
         }
     }
 
+    protected void startCurrentClient(int cookie) {
+        if (mCurrentClient == null) {
+            Slog.e(getTag(), "Trying to start null client!");
+            return;
+        }
+        if (DEBUG) Slog.v(getTag(), "starting client "
+                + mCurrentClient.getClass().getSuperclass().getSimpleName()
+                + "(" + mCurrentClient.getOwnerString() + ")"
+                + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
+        if (cookie != mCurrentClient.getCookie()) {
+            Slog.e(getTag(), "Mismatched cookie");
+            return;
+        }
+        notifyClientActiveCallbacks(true);
+        mCurrentClient.start();
+    }
+
     protected void removeClient(ClientMonitor client) {
         if (client != null) {
             client.destroy();
diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java
index a7ada2f..d19aff6 100644
--- a/services/core/java/com/android/server/biometrics/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java
@@ -58,6 +58,9 @@
 
     private IBinder mToken;
     private BiometricServiceBase.ServiceListener mListener;
+    // Currently only used for authentication client. The cookie generated by BiometricService
+    // is never 0.
+    private final int mCookie;
 
     protected final MetricsLogger mMetricsLogger;
     protected final Metrics mMetrics;
@@ -80,7 +83,7 @@
     public ClientMonitor(Context context, Metrics metrics,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
-            boolean restricted, String owner) {
+            boolean restricted, String owner, int cookie) {
         mContext = context;
         mMetrics = metrics;
         mDaemon = daemon;
@@ -91,6 +94,7 @@
         mGroupId = groupId;
         mIsRestricted = restricted;
         mOwner = owner;
+        mCookie = cookie;
         mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
         mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
         mMetricsLogger = new MetricsLogger();
@@ -107,6 +111,10 @@
         return mMetrics.logTag();
     }
 
+    public int getCookie() {
+        return mCookie;
+    }
+
     /**
      * Contacts the biometric's HAL to start the client.
      * @return 0 on success, errno from driver on failure
@@ -174,7 +182,7 @@
     public boolean onError(long deviceId, int error, int vendorCode) {
         try {
             if (mListener != null) {
-                mListener.onError(deviceId, error, vendorCode);
+                mListener.onError(deviceId, error, vendorCode, getCookie());
             }
         } catch (RemoteException e) {
             Slog.w(getLogTag(), "Failed to invoke sendError", e);
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 76dc5a9..f858ef5 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -40,7 +40,7 @@
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils) {
         super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
-                owner);
+                owner, 0 /* cookie */);
         mBiometricUtils = utils;
         mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
     }
diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java
index 47dc7ff..df6220c 100644
--- a/services/core/java/com/android/server/biometrics/EnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java
@@ -34,7 +34,7 @@
             BiometricServiceBase.ServiceListener listener, int groupId, int userId,
             boolean restricted, String owner) {
         super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
-                owner);
+                owner, 0 /* cookie */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java
index 15b3773..be233ec 100644
--- a/services/core/java/com/android/server/biometrics/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/RemovalClient.java
@@ -37,7 +37,7 @@
             BiometricServiceBase.ServiceListener listener, int biometricId, int groupId, int userId,
             boolean restricted, String owner, BiometricUtils utils) {
         super(context, metrics, daemon, halDeviceId, token, listener, userId, groupId, restricted,
-                owner);
+                owner, 0 /* cookie */);
         mBiometricId = biometricId;
         mBiometricUtils = utils;
     }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 7aa2e47..557af04 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -27,9 +27,8 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
 import android.hardware.biometrics.face.V1_0.Status;
@@ -38,7 +37,6 @@
 import android.hardware.face.IFaceService;
 import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -50,7 +48,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.biometrics.BiometricServiceBase;
@@ -89,27 +86,9 @@
         public FaceAuthClient(Context context,
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, Bundle bundle,
-                IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
-                boolean requireConfirmation) {
+                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, bundle, dialogReceiver, statusBarService,
-                    requireConfirmation);
-        }
-
-        @Override
-        public String getErrorString(int error, int vendorCode) {
-            return FaceManager.getErrorString(getContext(), error, vendorCode);
-        }
-
-        @Override
-        public String getAcquiredString(int acquireInfo, int vendorCode) {
-            return FaceManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
-        }
-
-        @Override
-        public int getBiometricType() {
-            return BiometricAuthenticator.TYPE_FACE;
+                    restricted, owner, cookie, requireConfirmation);
         }
     }
 
@@ -162,28 +141,33 @@
             final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    null /* bundle */, null /* dialogReceiver */, mStatusBarService,
-                    false /* requireConfirmation */);
+                    0 /* cookie */, false /* requireConfirmation */);
             authenticateInternal(client, opId, opPackageName);
         }
 
         @Override // Binder call
-        public void authenticateFromService(boolean requireConfirmation, IBinder token, long opId,
-                int groupId, IBiometricServiceReceiver receiver, int flags,
-                String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver,
-                int callingUid, int callingPid, int callingUserId) {
+        public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
+                int groupId, IBiometricServiceReceiverInternal wrapperReceiver,
+                String opPackageName, int cookie, int callingUid, int callingPid,
+                int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             final boolean restricted = true; // BiometricPrompt is always restricted
             final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token,
-                    new BiometricPromptServiceListenerImpl(receiver),
-                    mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    bundle, dialogReceiver, mStatusBarService, true /* requireConfirmation */);
+                    new BiometricPromptServiceListenerImpl(wrapperReceiver),
+                    mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
+                    true /* requireConfirmation */);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
 
         @Override // Binder call
+        public void startPreparedClient(int cookie) {
+            checkPermission(MANAGE_BIOMETRIC);
+            startCurrentClient(cookie);
+        }
+
+        @Override // Binder call
         public void cancelAuthentication(final IBinder token, final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             cancelAuthenticationInternal(token, opPackageName);
@@ -191,10 +175,10 @@
 
         @Override // Binder call
         public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
-                int callingUid, int callingPid, int callingUserId) {
+                int callingUid, int callingPid, int callingUserId, boolean fromClient) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            cancelAuthenticationInternal(token, opPackageName,
-                    callingUid, callingPid, callingUserId);
+            cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+                    callingUserId, fromClient);
         }
 
         @Override // Binder call
@@ -405,12 +389,9 @@
      * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
      * BiometricPrompt.
      */
-    private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
-        private IBiometricServiceReceiver mBiometricServiceReceiver;
-
-        public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
-            mBiometricServiceReceiver = receiver;
+    private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+        BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+            super(wrapperReceiver);
         }
 
         @Override
@@ -419,32 +400,18 @@
             /**
              * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
              */
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAcquired(deviceId,
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onAcquired(
                         FaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
                         FaceManager.getAcquiredString(getContext(), acquiredInfo, vendorCode));
             }
         }
 
         @Override
-        public void onAuthenticationSucceeded(long deviceId,
-                BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
-            }
-        }
-
-        @Override
-        public void onAuthenticationFailed(long deviceId) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
-            }
-        }
-
-        @Override
-        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onError(deviceId, error,
+        public void onError(long deviceId, int error, int vendorCode, int cookie)
+                throws RemoteException {
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onError(cookie, error,
                         FaceManager.getErrorString(getContext(), error, vendorCode));
             }
         }
@@ -455,7 +422,6 @@
      * the FaceManager.
      */
     private class ServiceListenerImpl implements ServiceListener {
-
         private IFaceServiceReceiver mFaceServiceReceiver;
 
         public ServiceListenerImpl(IFaceServiceReceiver receiver) {
@@ -501,7 +467,8 @@
         }
 
         @Override
-        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+        public void onError(long deviceId, int error, int vendorCode, int cookie)
+                throws RemoteException {
             if (mFaceServiceReceiver != null) {
                 mFaceServiceReceiver.onError(deviceId, error, vendorCode);
             }
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index b0b788f..6a5bc61 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -30,9 +30,8 @@
 import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.IBiometricPromptReceiver;
-import android.hardware.biometrics.IBiometricServiceReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
@@ -42,7 +41,6 @@
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.os.Binder;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -55,7 +53,6 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.DumpUtils;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.biometrics.AuthenticationClient;
@@ -109,27 +106,10 @@
         public FingerprintAuthClient(Context context,
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, Bundle bundle,
-                IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService,
+                boolean restricted, String owner, int cookie,
                 boolean requireConfirmation) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, bundle, dialogReceiver, statusBarService,
-                    requireConfirmation);
-        }
-
-        @Override
-        public String getErrorString(int error, int vendorCode) {
-            return FingerprintManager.getErrorString(getContext(), error, vendorCode);
-        }
-
-        @Override
-        public String getAcquiredString(int acquireInfo, int vendorCode) {
-            return FingerprintManager.getAcquiredString(getContext(), acquireInfo, vendorCode);
-        }
-
-        @Override
-        public int getBiometricType() {
-            return BiometricAuthenticator.TYPE_FINGERPRINT;
+                    restricted, owner, cookie, requireConfirmation);
         }
     }
 
@@ -182,38 +162,44 @@
             final boolean restricted = isRestricted();
             final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
-                    mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
-                    null /* dialogReceiver */, mStatusBarService, false /* requireConfirmation */);
+                    mCurrentUserId, groupId, opId, restricted, opPackageName,
+                    0 /* cookie */, false /* requireConfirmation */);
             authenticateInternal(client, opId, opPackageName);
         }
 
         @Override // Binder call
-        public void authenticateFromService(IBinder token, long opId, int groupId,
-                IBiometricServiceReceiver receiver, int flags, String opPackageName,
-                Bundle bundle, IBiometricPromptReceiver dialogReceiver,
-                int callingUid, int callingPid, int callingUserId) {
+        public void prepareForAuthentication(IBinder token, long opId, int groupId,
+                IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
+                int cookie, int callingUid, int callingPid, int callingUserId) {
             checkPermission(MANAGE_BIOMETRIC);
             final boolean restricted = true; // BiometricPrompt is always restricted
             final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token,
-                    new BiometricPromptServiceListenerImpl(receiver),
-                    mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
-                    dialogReceiver, mStatusBarService, false /* requireConfirmation */);
+                    new BiometricPromptServiceListenerImpl(wrapperReceiver),
+                    mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
+                    false /* requireConfirmation */);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
 
         @Override // Binder call
+        public void startPreparedClient(int cookie) {
+            checkPermission(MANAGE_BIOMETRIC);
+            startCurrentClient(cookie);
+        }
+
+
+        @Override // Binder call
         public void cancelAuthentication(final IBinder token, final String opPackageName) {
             cancelAuthenticationInternal(token, opPackageName);
         }
 
         @Override // Binder call
         public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
-                int callingUid, int callingPid, int callingUserId) {
+                int callingUid, int callingPid, int callingUserId, boolean fromClient) {
             checkPermission(MANAGE_BIOMETRIC);
-            cancelAuthenticationInternal(token, opPackageName,
-                    callingUid, callingPid, callingUserId);
+            cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid,
+                    callingUserId, fromClient);
         }
 
         @Override // Binder call
@@ -388,43 +374,25 @@
      * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
      * BiometricPrompt.
      */
-    private class BiometricPromptServiceListenerImpl implements ServiceListener {
-
-        private IBiometricServiceReceiver mBiometricServiceReceiver;
-
-        public BiometricPromptServiceListenerImpl(IBiometricServiceReceiver receiver) {
-            mBiometricServiceReceiver = receiver;
+    private class BiometricPromptServiceListenerImpl extends BiometricServiceListener {
+        BiometricPromptServiceListenerImpl(IBiometricServiceReceiverInternal wrapperReceiver) {
+            super(wrapperReceiver);
         }
 
         @Override
         public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
                 throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAcquired(deviceId, acquiredInfo,
-                        FingerprintManager.getAcquiredString(
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onAcquired(acquiredInfo, FingerprintManager.getAcquiredString(
                             getContext(), acquiredInfo, vendorCode));
             }
         }
 
         @Override
-        public void onAuthenticationSucceeded(long deviceId,
-                BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAuthenticationSucceeded(deviceId);
-            }
-        }
-
-        @Override
-        public void onAuthenticationFailed(long deviceId) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onAuthenticationFailed(deviceId);
-            }
-        }
-
-        @Override
-        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
-            if (mBiometricServiceReceiver != null) {
-                mBiometricServiceReceiver.onError(deviceId, error,
+        public void onError(long deviceId, int error, int vendorCode, int cookie)
+                throws RemoteException {
+            if (getWrapperReceiver() != null) {
+                getWrapperReceiver().onError(cookie, error,
                         FingerprintManager.getErrorString(getContext(), error, vendorCode));
             }
         }
@@ -435,7 +403,6 @@
      * the FingerprintManager.
      */
     private class ServiceListenerImpl implements ServiceListener {
-
         private IFingerprintServiceReceiver mFingerprintServiceReceiver;
 
         public ServiceListenerImpl(IFingerprintServiceReceiver receiver) {
@@ -483,7 +450,8 @@
         }
 
         @Override
-        public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+        public void onError(long deviceId, int error, int vendorCode, int cookie)
+                throws RemoteException {
             if (mFingerprintServiceReceiver != null) {
                 mFingerprintServiceReceiver.onError(deviceId, error, vendorCode);
             }
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index c3e3842..bf95210 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -72,6 +72,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Protocol;
+import com.android.internal.util.RingBufferIndices;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
@@ -99,7 +100,7 @@
     private static final String TAG = NetworkMonitor.class.getSimpleName();
     private static final boolean DBG  = true;
     private static final boolean VDBG = false;
-
+    private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
     // Default configuration values for captive portal detection probes.
     // TODO: append a random length parameter to the default HTTPS url.
     // TODO: randomize browser version ids in the default User-Agent String.
@@ -116,6 +117,15 @@
     private static final int SOCKET_TIMEOUT_MS = 10000;
     private static final int PROBE_TIMEOUT_MS  = 3000;
 
+    // Default configuration values for data stall detection.
+    private static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
+    private static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
+    private static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
+
+    private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+    private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
+            (1 << DATA_STALL_EVALUATION_TYPE_DNS);
+
     static enum EvaluationResult {
         VALIDATED(true),
         CAPTIVE_PORTAL(false);
@@ -233,6 +243,12 @@
      */
     public static final int CMD_PROBE_COMPLETE = BASE + 16;
 
+    /**
+     * ConnectivityService notifies NetworkMonitor of DNS query responses event.
+     * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
+     */
+    public static final int EVENT_DNS_NOTIFICATION = BASE + 17;
+
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
     private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
@@ -297,6 +313,7 @@
     private final State mCaptivePortalState = new CaptivePortalState();
     private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
     private final State mProbingState = new ProbingState();
+    private final State mWaitingForNextProbeState = new WaitingForNextProbeState();
 
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
 
@@ -314,6 +331,12 @@
     private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
     private int mEvaluateAttempts = 0;
     private volatile int mProbeToken = 0;
+    private final int mConsecutiveDnsTimeoutThreshold;
+    private final int mDataStallMinEvaluateTime;
+    private final int mDataStallValidDnsTimeThreshold;
+    private final int mDataStallEvaluationType;
+    private final DnsStallDetector mDnsStallDetector;
+    private long mLastProbeTime;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
@@ -346,6 +369,7 @@
         addState(mMaybeNotifyState, mDefaultState);
             addState(mEvaluatingState, mMaybeNotifyState);
                 addState(mProbingState, mEvaluatingState);
+                addState(mWaitingForNextProbeState, mEvaluatingState);
             addState(mCaptivePortalState, mMaybeNotifyState);
         addState(mEvaluatingPrivateDnsState, mDefaultState);
         addState(mValidatedState, mDefaultState);
@@ -359,6 +383,12 @@
         mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
         mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
         mRandom = deps.getRandom();
+        // TODO: Evaluate to move data stall configuration to a specific class.
+        mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold();
+        mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold);
+        mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
+        mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
+        mDataStallEvaluationType = getDataStallEvalutionType();
 
         start();
     }
@@ -507,6 +537,9 @@
                     sendMessage(CMD_EVALUATE_PRIVATE_DNS);
                     break;
                 }
+                case EVENT_DNS_NOTIFICATION:
+                    mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+                    break;
                 default:
                     break;
             }
@@ -537,6 +570,13 @@
                 case CMD_EVALUATE_PRIVATE_DNS:
                     transitionTo(mEvaluatingPrivateDnsState);
                     break;
+                case EVENT_DNS_NOTIFICATION:
+                    mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
+                    if (isDataStall()) {
+                        validationLog("Suspecting data stall, reevaluate");
+                        transitionTo(mEvaluatingState);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -839,6 +879,11 @@
 
         @Override
         public void enter() {
+            if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
+                //Don't continue to blame UID forever.
+                TrafficStats.clearThreadStatsUid();
+            }
+
             final int token = ++mProbeToken;
             mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
                     isCaptivePortal())));
@@ -856,6 +901,7 @@
 
                     final CaptivePortalProbeResult probeResult =
                             (CaptivePortalProbeResult) message.obj;
+                    mLastProbeTime = SystemClock.elapsedRealtime();
                     if (probeResult.isSuccessful()) {
                         // Transit EvaluatingPrivateDnsState to get to Validated
                         // state (even if no Private DNS validation required).
@@ -865,28 +911,16 @@
                         mLastPortalProbeResult = probeResult;
                         transitionTo(mCaptivePortalState);
                     } else {
-                        final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
-                        sendMessageDelayed(msg, mReevaluateDelayMs);
                         logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
                         notifyNetworkTestResultInvalid(probeResult.redirectUrl);
-                        if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
-                            // Don't continue to blame UID forever.
-                            TrafficStats.clearThreadStatsUid();
-                        }
-                        mReevaluateDelayMs *= 2;
-                        if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
-                            mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
-                        }
+                        transitionTo(mWaitingForNextProbeState);
                     }
                     return HANDLED;
-                case CMD_REEVALUATE:
-                    // Leave the event to EvaluatingState. Defer this message will result in reset
-                    // of mReevaluateDelayMs and mEvaluateAttempts.
-                case CMD_NETWORK_DISCONNECTED:
+                case EVENT_DNS_NOTIFICATION:
+                    // Leave the event to DefaultState to record correct dns timestamp.
                     return NOT_HANDLED;
                 default:
-                    // TODO: Some events may able to handle in this state, instead of deferring to
-                    // next state.
+                    // Wait for probe result and defer events to next state by default.
                     deferMessage(message);
                     return HANDLED;
             }
@@ -901,6 +935,29 @@
         }
     }
 
+    // Being in the WaitingForNextProbeState indicates that evaluating probes failed and state is
+    // transited from ProbingState. This ensures that the state machine is only in ProbingState
+    // while a probe is in progress, not while waiting to perform the next probe. That allows
+    // ProbingState to defer most messages until the probe is complete, which keeps the code simple
+    // and matches the pre-Q behaviour where probes were a blocking operation performed on the state
+    // machine thread.
+    private class WaitingForNextProbeState extends State {
+        @Override
+        public void enter() {
+            final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
+            sendMessageDelayed(msg, mReevaluateDelayMs);
+            mReevaluateDelayMs *= 2;
+            if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
+                mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            return NOT_HANDLED;
+        }
+    }
+
     // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
@@ -947,6 +1004,29 @@
                 Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
     }
 
+    private int getConsecutiveDnsTimeoutThreshold() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
+                DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD);
+    }
+
+    private int getDataStallMinEvaluateTime() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL,
+                DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS);
+    }
+
+    private int getDataStallValidDnsTimeThreshold() {
+        return mDependencies.getSetting(mContext,
+                Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD,
+                DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
+    }
+
+    private int getDataStallEvalutionType() {
+        return mDependencies.getSetting(mContext, Settings.Global.DATA_STALL_EVALUATION_TYPE,
+                DEFAULT_DATA_STALL_EVALUATION_TYPES);
+    }
+
     // Static for direct access by ConnectivityService
     public static String getCaptivePortalServerHttpUrl(Context context) {
         return getCaptivePortalServerHttpUrl(Dependencies.DEFAULT, context);
@@ -1462,4 +1542,127 @@
 
         public static final Dependencies DEFAULT = new Dependencies();
     }
+
+    /**
+     * Methods in this class perform no locking because all accesses are performed on the state
+     * machine's thread. Need to consider the thread safety if it ever could be accessed outside the
+     * state machine.
+     */
+    @VisibleForTesting
+    protected class DnsStallDetector {
+        private static final int DEFAULT_DNS_LOG_SIZE = 50;
+        private int mConsecutiveTimeoutCount = 0;
+        private int mSize;
+        final DnsResult[] mDnsEvents;
+        final RingBufferIndices mResultIndices;
+
+        DnsStallDetector(int size) {
+            mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size);
+            mDnsEvents = new DnsResult[mSize];
+            mResultIndices = new RingBufferIndices(mSize);
+        }
+
+        @VisibleForTesting
+        protected void accumulateConsecutiveDnsTimeoutCount(int code) {
+            final DnsResult result = new DnsResult(code);
+            mDnsEvents[mResultIndices.add()] = result;
+            if (result.isTimeout()) {
+                mConsecutiveTimeoutCount++;
+            } else {
+                // Keep the event in mDnsEvents without clearing it so that there are logs to do the
+                // simulation and analysis.
+                mConsecutiveTimeoutCount = 0;
+            }
+        }
+
+        private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) {
+            if (timeoutCountThreshold <= 0) {
+                Log.wtf(TAG, "Timeout count threshold should be larger than 0.");
+                return false;
+            }
+
+            // Check if the consecutive timeout count reach the threshold or not.
+            if (mConsecutiveTimeoutCount < timeoutCountThreshold) {
+                return false;
+            }
+
+            // Check if the target dns event index is valid or not.
+            final int firstConsecutiveTimeoutIndex =
+                    mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold);
+
+            // If the dns timeout events happened long time ago, the events are meaningless for
+            // data stall evaluation. Thus, check if the first consecutive timeout dns event
+            // considered in the evaluation happened in defined threshold time.
+            final long now = SystemClock.elapsedRealtime();
+            final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp;
+            return (firstTimeoutTime < validTime);
+        }
+
+        int getConsecutiveTimeoutCount() {
+            return mConsecutiveTimeoutCount;
+        }
+    }
+
+    private static class DnsResult {
+        // TODO: Need to move the DNS return code definition to a specific class once unify DNS
+        // response code is done.
+        private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
+        private final long mTimeStamp;
+        private final int mReturnCode;
+
+        DnsResult(int code) {
+            mTimeStamp = SystemClock.elapsedRealtime();
+            mReturnCode = code;
+        }
+
+        private boolean isTimeout() {
+            return mReturnCode == RETURN_CODE_DNS_TIMEOUT;
+        }
+    }
+
+
+    @VisibleForTesting
+    protected DnsStallDetector getDnsStallDetector() {
+        return mDnsStallDetector;
+    }
+
+    private boolean dataStallEvaluateTypeEnabled(int type) {
+        return (mDataStallEvaluationType & (1 << type)) != 0;
+    }
+
+    @VisibleForTesting
+    protected long getLastProbeTime() {
+        return mLastProbeTime;
+    }
+
+    @VisibleForTesting
+    protected boolean isDataStall() {
+        boolean result = false;
+        // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
+        // possible traffic cost in metered network.
+        if (mNetworkAgentInfo.networkCapabilities.isMetered()
+                && (SystemClock.elapsedRealtime() - getLastProbeTime()
+                < mDataStallMinEvaluateTime)) {
+            return false;
+        }
+
+        // Check dns signal. Suspect it may be a data stall if both :
+        // 1. The number of consecutive DNS query timeouts > mConsecutiveDnsTimeoutThreshold.
+        // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
+        if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
+            if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
+                    mDataStallValidDnsTimeThreshold)) {
+                result = true;
+                logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
+            }
+        }
+
+        if (VDBG_STALL) {
+            log("isDataStall: result=" + result + ", consecutive dns timeout count="
+                    + mDnsStallDetector.getConsecutiveTimeoutCount());
+        }
+
+        return result;
+    }
 }
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index 33525fd..f2c539c 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -16,16 +16,7 @@
 
 package com.android.server.display;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
 import android.content.Context;
-import android.graphics.PixelFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
@@ -34,20 +25,29 @@
 import android.opengl.EGLContext;
 import android.opengl.EGLDisplay;
 import android.opengl.EGLSurface;
-import android.opengl.GLES20;
 import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
 import android.util.Slog;
 import android.view.DisplayInfo;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 
-import libcore.io.Streams;
-
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
 
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
 /**
  * <p>
  * Animates a screen transition from on to off or off to on by applying
@@ -569,37 +569,31 @@
             mSurfaceSession = new SurfaceSession();
         }
 
-        SurfaceControl.openTransaction();
-        try {
-            if (mSurfaceControl == null) {
-                try {
-                    int flags;
-                    if (mMode == MODE_FADE) {
-                        flags = SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN;
-                    } else {
-                        flags = SurfaceControl.OPAQUE | SurfaceControl.HIDDEN;
-                    }
-                    mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
-                            .setName("ColorFade")
-                            .setSize(mDisplayWidth, mDisplayHeight)
-                            .setFlags(flags)
-                            .build();
-                } catch (OutOfResourcesException ex) {
-                    Slog.e(TAG, "Unable to create surface.", ex);
-                    return false;
+        if (mSurfaceControl == null) {
+            Transaction t = new Transaction();
+            try {
+                final SurfaceControl.Builder builder =
+                        new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade");
+                if (mMode == MODE_FADE) {
+                    builder.setColorLayer(true);
+                } else {
+                    builder.setBufferSize(mDisplayWidth, mDisplayHeight);
                 }
-
-                mSurfaceControl.setLayerStack(mDisplayLayerStack);
-                mSurfaceControl.setSize(mDisplayWidth, mDisplayHeight);
-                mSurface = new Surface();
-                mSurface.copyFrom(mSurfaceControl);
-
-                mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
-                        mDisplayId, mSurfaceControl);
-                mSurfaceLayout.onDisplayTransaction();
+                mSurfaceControl = builder.build();
+            } catch (OutOfResourcesException ex) {
+                Slog.e(TAG, "Unable to create surface.", ex);
+                return false;
             }
-        } finally {
-            SurfaceControl.closeTransaction();
+
+            t.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+            t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+            mSurface = new Surface();
+            mSurface.copyFrom(mSurfaceControl);
+
+            mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+                    mDisplayId, mSurfaceControl);
+            mSurfaceLayout.onDisplayTransaction(t);
+            t.apply();
         }
         return true;
     }
@@ -746,7 +740,7 @@
         }
 
         @Override
-        public void onDisplayTransaction() {
+        public void onDisplayTransaction(Transaction t) {
             synchronized (this) {
                 if (mSurfaceControl == null) {
                     return;
@@ -755,21 +749,21 @@
                 DisplayInfo displayInfo = mDisplayManagerInternal.getDisplayInfo(mDisplayId);
                 switch (displayInfo.rotation) {
                     case Surface.ROTATION_0:
-                        mSurfaceControl.setPosition(0, 0);
-                        mSurfaceControl.setMatrix(1, 0, 0, 1);
+                        t.setPosition(mSurfaceControl, 0, 0);
+                        t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
                         break;
                     case Surface.ROTATION_90:
-                        mSurfaceControl.setPosition(0, displayInfo.logicalHeight);
-                        mSurfaceControl.setMatrix(0, -1, 1, 0);
+                        t.setPosition(mSurfaceControl, 0, displayInfo.logicalHeight);
+                        t.setMatrix(mSurfaceControl, 0, -1, 1, 0);
                         break;
                     case Surface.ROTATION_180:
-                        mSurfaceControl.setPosition(displayInfo.logicalWidth,
+                        t.setPosition(mSurfaceControl, displayInfo.logicalWidth,
                                 displayInfo.logicalHeight);
-                        mSurfaceControl.setMatrix(-1, 0, 0, -1);
+                        t.setMatrix(mSurfaceControl, -1, 0, 0, -1);
                         break;
                     case Surface.ROTATION_270:
-                        mSurfaceControl.setPosition(displayInfo.logicalWidth, 0);
-                        mSurfaceControl.setMatrix(0, 1, -1, 0);
+                        t.setPosition(mSurfaceControl, displayInfo.logicalWidth, 0);
+                        t.setMatrix(mSurfaceControl, 0, 1, -1, 0);
                         break;
                 }
             }
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 7bfe9ce..6ee5665 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -225,6 +225,8 @@
         viewport.deviceHeight = isRotated ? info.width : info.height;
 
         viewport.uniqueId = info.uniqueId;
+        // TODO(b/112898898) Use an actual port here.
+        viewport.physicalPort = null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d04fa23..360a7d1 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -497,7 +497,7 @@
 
         // List is self-synchronized copy-on-write.
         for (DisplayTransactionListener listener : mDisplayTransactionListeners) {
-            listener.onDisplayTransaction();
+            listener.onDisplayTransaction(t);
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index b148a2f..c0d3fdf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -980,7 +980,7 @@
      * @param sourceAddress a logical address of source device where sends polling message
      * @param pickStrategy strategy how to pick polling candidates
      * @param retryCount the number of retry used to send polling message to remote devices
-     * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
+     * @throws IllegalArgumentException if {@code pickStrategy} is invalid value
      */
     @ServiceThreadOnly
     void pollDevices(DevicePollingCallback callback, int sourceAddress, int pickStrategy,
diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java
new file mode 100644
index 0000000..970e86a
--- /dev/null
+++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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.input;
+
+import android.text.TextUtils;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
+
+class ConfigurationProcessor {
+    private static final String TAG = "ConfigurationProcessor";
+
+    static List<String> processExcludedDeviceNames(InputStream xml) throws Exception {
+        List<String> names = new ArrayList<>();
+        try (InputStreamReader confReader = new InputStreamReader(xml)) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(confReader);
+            XmlUtils.beginDocument(parser, "devices");
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (!"device".equals(parser.getName())) {
+                    break;
+                }
+                String name = parser.getAttributeValue(null, "name");
+                if (name != null) {
+                    names.add(name);
+                }
+            }
+        }
+        return names;
+    }
+
+    /**
+     * Parse the configuration for input port associations.
+     *
+     * Configuration format:
+     * <code>
+     * &lt;ports>
+     *     &lt;port display="0" input="usb-xhci-hcd.0.auto-1.4.3/input0" />
+     *     &lt;port display="1" input="usb-xhci-hcd.0.auto-1.4.2/input0" />
+     * &lt;/ports>
+     * </code>
+     *
+     * In this example, any input device that has physical port of
+     * "usb-xhci-hcd.0.auto-1.4.3/input0" will be associated with a display
+     * that has the physical port "0". If such a display does not exist, the input device
+     * will be disabled and no input events will be dispatched from that input device until a
+     * matching display appears. Likewise, an input device that has port "..1.4.2.." will have
+     * its input events forwarded to a display that has physical port of "1".
+     *
+     * Note: display port must be a numeric value, and this is checked at runtime for validity.
+     * At the same time, it is specified as a string for simplicity.
+     *
+     * Note: do not confuse "display id" with "display port".
+     * The "display port" is the physical port on which the display is connected. This could
+     * be something like HDMI0, HDMI1, etc. For virtual displays, "display port" will be null.
+     * The "display id" is a way to identify a particular display, and is not a stable API.
+     * All displays, including virtual ones, will have a display id.
+     *
+     * Return the pairs of associations. The first item in the pair is the input port,
+     * the second item in the pair is the display port.
+     */
+    @VisibleForTesting
+    static List<Pair<String, String>> processInputPortAssociations(InputStream xml)
+            throws Exception {
+        List<Pair<String, String>> associations = new ArrayList<>();
+        try (InputStreamReader confReader = new InputStreamReader(xml)) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(confReader);
+            XmlUtils.beginDocument(parser, "ports");
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+                String entryName = parser.getName();
+                if (!"port".equals(entryName)) {
+                    break;
+                }
+                String inputPort = parser.getAttributeValue(null, "input");
+                String displayPort = parser.getAttributeValue(null, "display");
+                if (TextUtils.isEmpty(inputPort) || TextUtils.isEmpty(displayPort)) {
+                    // This is likely an error by an OEM during device configuration
+                    Slog.wtf(TAG, "Ignoring incomplete entry");
+                    continue;
+                }
+                try {
+                    Integer.parseUnsignedInt(displayPort);
+                } catch (NumberFormatException e) {
+                    Slog.wtf(TAG, "Display port should be an integer");
+                    continue;
+                }
+                associations.add(new Pair<>(inputPort, displayPort));
+            }
+        }
+        return associations;
+    }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3a31c9c..d96b6cb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -64,18 +64,18 @@
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.Xml;
 import android.view.Display;
 import android.view.IInputFilter;
 import android.view.IInputFilterHost;
 import android.view.IWindow;
-import android.view.InputChannel;
 import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
+import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
+import android.view.InputWindowHandle;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
 import android.view.Surface;
@@ -97,14 +97,13 @@
 import libcore.io.IoUtils;
 import libcore.io.Streams;
 
-import org.xmlpull.v1.XmlPullParser;
-
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -124,6 +123,7 @@
     static final boolean DEBUG = false;
 
     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+    private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
 
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
     private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2;
@@ -1443,25 +1443,10 @@
         }
     }
 
-    public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
-        nativeSetInputWindows(mPtr, windowHandles, displayId);
-    }
-
     public void setFocusedApplication(int displayId, InputApplicationHandle application) {
         nativeSetFocusedApplication(mPtr, displayId, application);
     }
 
-    public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
-        final IWindow newFocusedWindow =
-            focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
-        if (mFocusedWindow != newFocusedWindow) {
-            if (mFocusedWindowHasCapture) {
-                setPointerCapture(false);
-            }
-            mFocusedWindow = newFocusedWindow;
-        }
-    }
-
     public void setFocusedDisplay(int displayId) {
         nativeSetFocusedDisplay(mPtr, displayId);
     }
@@ -1799,11 +1784,22 @@
         mWindowManagerCallbacks.notifyInputChannelBroken(token);
     }
 
+    // Native callback
+    private void notifyFocusChanged(IBinder token) {
+        if (mFocusedWindow != token) {
+            if (mFocusedWindowHasCapture) {
+                setPointerCapture(false);
+            }
+            if (token instanceof IWindow) {
+                mFocusedWindow = (IWindow) token;
+            }
+        }
+    }
+
     // Native callback.
-    private long notifyANR(InputApplicationHandle inputApplicationHandle,
-            IBinder token, String reason) {
+    private long notifyANR(IBinder token, String reason) {
         return mWindowManagerCallbacks.notifyANR(
-                inputApplicationHandle, token, reason);
+                token, reason);
     }
 
     // Native callback.
@@ -1834,14 +1830,12 @@
     }
 
     // Native callback.
-    private long interceptKeyBeforeDispatching(IBinder focus,
-            KeyEvent event, int policyFlags) {
+    private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
     }
 
     // Native callback.
-    private KeyEvent dispatchUnhandledKey(IBinder focus,
-            KeyEvent event, int policyFlags) {
+    private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
         return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
     }
 
@@ -1858,11 +1852,9 @@
     }
 
     // Native callback.
-    private String[] getExcludedDeviceNames() {
-        ArrayList<String> names = new ArrayList<String>();
-
+    private static String[] getExcludedDeviceNames() {
+        List<String> names = new ArrayList<>();
         // Read partner-provided list of excluded input devices
-        XmlPullParser parser = null;
         // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
         final File[] baseDirs = {
             Environment.getRootDirectory(),
@@ -1870,33 +1862,52 @@
         };
         for (File baseDir: baseDirs) {
             File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
-            FileReader confreader = null;
             try {
-                confreader = new FileReader(confFile);
-                parser = Xml.newPullParser();
-                parser.setInput(confreader);
-                XmlUtils.beginDocument(parser, "devices");
-
-                while (true) {
-                    XmlUtils.nextElement(parser);
-                    if (!"device".equals(parser.getName())) {
-                        break;
-                    }
-                    String name = parser.getAttributeValue(null, "name");
-                    if (name != null) {
-                        names.add(name);
-                    }
-                }
+                InputStream stream = new FileInputStream(confFile);
+                names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
             } catch (FileNotFoundException e) {
                 // It's ok if the file does not exist.
             } catch (Exception e) {
-                Slog.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
-            } finally {
-                try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+                Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
             }
         }
+        return names.toArray(new String[0]);
+    }
 
-        return names.toArray(new String[names.size()]);
+    /**
+     * Flatten a list of pairs into a list, with value positioned directly next to the key
+     * @return Flattened list
+     */
+    private static <T> List<T> flatten(@NonNull List<Pair<T, T>> pairs) {
+        List<T> list = new ArrayList<>(pairs.size() * 2);
+        for (Pair<T, T> pair : pairs) {
+            list.add(pair.first);
+            list.add(pair.second);
+        }
+        return list;
+    }
+
+    /**
+     * Ports are highly platform-specific, so only allow these to be specified in the vendor
+     * directory.
+     */
+    // Native callback
+    private static String[] getInputPortAssociations() {
+        File baseDir = Environment.getVendorDirectory();
+        File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
+
+        try {
+            InputStream stream = new FileInputStream(confFile);
+            List<Pair<String, String>> associations =
+                    ConfigurationProcessor.processInputPortAssociations(stream);
+            List<String> associationList = flatten(associations);
+            return associationList.toArray(new String[0]);
+        } catch (FileNotFoundException e) {
+            // Most of the time, file will not exist, which is expected.
+        } catch (Exception e) {
+            Slog.e(TAG, "Could not parse '" + confFile.getAbsolutePath() + "'", e);
+        }
+        return new String[0];
     }
 
     // Native callback.
@@ -1993,8 +2004,7 @@
 
         public void notifyInputChannelBroken(IBinder token);
 
-        public long notifyANR(InputApplicationHandle inputApplicationHandle,
-                IBinder token, String reason);
+        public long notifyANR(IBinder token, String reason);
 
         public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1dfb86a..a8da968 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2420,8 +2420,9 @@
                         .setContentText(summary)
                         .setContentIntent(mImeSwitchPendingIntent);
                 try {
+                    // TODO(b/120076400): Figure out what is the best behavior
                     if ((mNotificationManager != null)
-                            && !mIWindowManager.hasNavigationBar()) {
+                            && !mIWindowManager.hasNavigationBar(DEFAULT_DISPLAY)) {
                         if (DEBUG) {
                             Slog.d(TAG, "--- show notification: label =  " + summary);
                         }
diff --git a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
index 6fe6324..d5be26a 100644
--- a/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/intelligence/IntelligenceManagerInternal.java
@@ -27,12 +27,13 @@
  *
  * @hide Only for use within the system server.
  */
+//TODO(b/111276913): rename once the final name is defined
 public abstract class IntelligenceManagerInternal {
 
     /**
      * Checks whether the given {@code uid} owns the
-     * {@link android.service.intelligence.IntelligenceService} implementation associated with the
-     * given {@code userId}.
+     * {@link android.service.intelligence.SmartSuggestionsService} implementation associated with
+     * the given {@code userId}.
      */
     public abstract boolean isIntelligenceServiceForUser(int uid, @UserIdInt int userId);
 
diff --git a/services/core/java/com/android/server/location/gps_debug.conf b/services/core/java/com/android/server/location/gps_debug.conf
new file mode 100644
index 0000000..34ce96f
--- /dev/null
+++ b/services/core/java/com/android/server/location/gps_debug.conf
@@ -0,0 +1,52 @@
+# Sample file for use for on device debug override only
+# Prefer frameworks/base/core/res/res/values/config.xml and
+# frameworks/base/core/res/res/values-mcc*-mnc*/config.xml
+
+################################
+##### AGPS server settings #####
+################################
+# FOR SUPL SUPPORT, set the following
+# SUPL_HOST=supl.google.com or IP
+# SUPL_PORT=7275
+
+# supl version 2.0
+# SUPL_VER=0x20000
+
+#SUPL_MODE is a bit mask set in config.xml per carrier by default.
+#If it is uncommented here, this value will overwrite the value from
+#config.xml.
+#MSA=0X2
+#MSB=0X1
+#SUPL_MODE=1
+
+# Emergency SUPL, 1=enable, 0=disable
+#SUPL_ES=0
+
+#Choose PDN for Emergency SUPL
+#1 - Use emergency PDN
+#0 - Use regular SUPL PDN for Emergency SUPL
+#USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0
+
+####################################
+#  LTE Positioning Profile Settings
+####################################
+# 0: Enable RRLP on LTE(Default)
+# 1: Enable LPP_User_Plane on LTE
+# 2: Enable LPP_Control_Plane
+# 3: Enable both LPP_User_Plane and LPP_Control_Plane
+#LPP_PROFILE = 2
+
+##################################################
+# Select Positioning Protocol on A-GLONASS system
+##################################################
+# 0x1: RRC CPlane
+# 0x2: RRLP UPlane
+# 0x4: LLP Uplane
+#A_GLONASS_POS_PROTOCOL_SELECT = 0
+
+# Below bit mask configures how GPS functionalities
+# should be locked when user turns off GPS on Settings
+# Set bit 0x1 if MO GPS functionalities are to be locked
+# Set bit 0x2 if NI GPS functionalities are to be locked
+# default - non is locked for backward compatibility
+#GPS_LOCK = 0
diff --git a/services/core/java/com/android/server/locksettings/SP800Derive.java b/services/core/java/com/android/server/locksettings/SP800Derive.java
new file mode 100644
index 0000000..77561fc
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/SP800Derive.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.locksettings;
+
+import java.nio.ByteBuffer;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Implementation of NIST SP800-108
+ * "Recommendation for Key Derivation Using Pseudorandom Functions"
+ * Hardcoded:
+ * [PRF=HMAC_SHA256]
+ * [CTRLOCATION=BEFORE_FIXED]
+ * [RLEN=32_BITS]
+ * L = 256
+ * L suffix: 32 bits
+ */
+class SP800Derive {
+    private final byte[] mKeyBytes;
+
+    SP800Derive(byte[] keyBytes) {
+        mKeyBytes = keyBytes;
+    }
+
+    private Mac getMac() {
+        try {
+            final Mac m = Mac.getInstance("HmacSHA256");
+            m.init(new SecretKeySpec(mKeyBytes, m.getAlgorithm()));
+            return m;
+        } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void update32(Mac m, int v) {
+        m.update(ByteBuffer.allocate(Integer.BYTES).putInt(v).array());
+    }
+
+    /**
+     *  Generate output from a single, fixed input.
+     */
+    public byte[] fixedInput(byte[] fixedInput) {
+        final Mac m = getMac();
+        update32(m, 1); // Hardwired counter value
+        m.update(fixedInput);
+        return m.doFinal();
+    }
+
+    /**
+     * Generate output from a label and context. We add a length field at the end of the context to
+     * disambiguate it from the length even in the presence of zero bytes.
+     */
+    public byte[] withContext(byte[] label, byte[] context) {
+        final Mac m = getMac();
+        // Hardwired counter value: 1
+        update32(m, 1); // Hardwired counter value
+        m.update(label);
+        m.update((byte) 0);
+        m.update(context);
+        update32(m, context.length * 8); // Disambiguate context
+        update32(m, 256); // Hardwired output length
+        return m.doFinal();
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 596daeb..d32c299 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -26,9 +26,9 @@
 import android.hardware.weaver.V1_0.WeaverReadResponse;
 import android.hardware.weaver.V1_0.WeaverReadStatus;
 import android.hardware.weaver.V1_0.WeaverStatus;
-import android.security.GateKeeper;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.security.GateKeeper;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.util.ArrayMap;
@@ -102,7 +102,8 @@
     private static final int INVALID_WEAVER_SLOT = -1;
 
     private static final byte SYNTHETIC_PASSWORD_VERSION_V1 = 1;
-    private static final byte SYNTHETIC_PASSWORD_VERSION = 2;
+    private static final byte SYNTHETIC_PASSWORD_VERSION_V2 = 2;
+    private static final byte SYNTHETIC_PASSWORD_VERSION_V3 = 3;
     private static final byte SYNTHETIC_PASSWORD_PASSWORD_BASED = 0;
     private static final byte SYNTHETIC_PASSWORD_TOKEN_BASED = 1;
 
@@ -128,6 +129,8 @@
     private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
     private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
     private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
+    private static final byte[] PERSONALISATION_CONTEXT =
+        "android-synthetic-password-personalization-context".getBytes();
 
     static class AuthenticationResult {
         public AuthenticationToken authToken;
@@ -136,6 +139,7 @@
     }
 
     static class AuthenticationToken {
+        private final byte mVersion;
         /*
          * Here is the relationship between all three fields:
          * P0 and P1 are two randomly-generated blocks. P1 is stored on disk but P0 is not.
@@ -146,29 +150,38 @@
         private @Nullable byte[] P1;
         private @NonNull String syntheticPassword;
 
+        AuthenticationToken(byte version) {
+            mVersion = version;
+        }
+
+        private byte[] derivePassword(byte[] personalization) {
+            if (mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+                return (new SP800Derive(syntheticPassword.getBytes()))
+                    .withContext(personalization, PERSONALISATION_CONTEXT);
+            } else {
+                return SyntheticPasswordCrypto.personalisedHash(personalization,
+                        syntheticPassword.getBytes());
+            }
+        }
+
         public String deriveKeyStorePassword() {
-            return bytesToHex(SyntheticPasswordCrypto.personalisedHash(
-                    PERSONALIZATION_KEY_STORE_PASSWORD, syntheticPassword.getBytes()));
+            return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD));
         }
 
         public byte[] deriveGkPassword() {
-            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_SP_GK_AUTH,
-                    syntheticPassword.getBytes());
+            return derivePassword(PERSONALIZATION_SP_GK_AUTH);
         }
 
         public byte[] deriveDiskEncryptionKey() {
-            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_FBE_KEY,
-                    syntheticPassword.getBytes());
+            return derivePassword(PERSONALIZATION_FBE_KEY);
         }
 
         public byte[] deriveVendorAuthSecret() {
-            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
-                    syntheticPassword.getBytes());
+            return derivePassword(PERSONALIZATION_AUTHSECRET_KEY);
         }
 
         public byte[] derivePasswordHashFactor() {
-            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_PASSWORD_HASH,
-                    syntheticPassword.getBytes());
+            return derivePassword(PERSONALIZATION_PASSWORD_HASH);
         }
 
         private void initialize(byte[] P0, byte[] P1) {
@@ -185,7 +198,7 @@
         }
 
         protected static AuthenticationToken create() {
-            AuthenticationToken result = new AuthenticationToken();
+            AuthenticationToken result = new AuthenticationToken(SYNTHETIC_PASSWORD_VERSION_V3);
             result.initialize(secureRandom(SYNTHETIC_PASSWORD_LENGTH),
                     secureRandom(SYNTHETIC_PASSWORD_LENGTH));
             return result;
@@ -802,7 +815,16 @@
         }
         byte[] content = createSPBlob(getHandleName(handle), secret, applicationId, sid);
         byte[] blob = new byte[content.length + 1 + 1];
-        blob[0] = SYNTHETIC_PASSWORD_VERSION;
+        /*
+         * We can upgrade from v1 to v2 because that's just a change in the way that
+         * the SP is stored. However, we can't upgrade to v3 because that is a change
+         * in the way that passwords are derived from the SP.
+         */
+        if (authToken.mVersion == SYNTHETIC_PASSWORD_VERSION_V3) {
+            blob[0] = SYNTHETIC_PASSWORD_VERSION_V3;
+        } else {
+            blob[0] = SYNTHETIC_PASSWORD_VERSION_V2;
+        }
         blob[1] = type;
         System.arraycopy(content, 0, blob, 2, content.length);
         saveState(SP_BLOB_NAME, blob, handle, userId);
@@ -940,7 +962,9 @@
             return null;
         }
         final byte version = blob[0];
-        if (version != SYNTHETIC_PASSWORD_VERSION && version != SYNTHETIC_PASSWORD_VERSION_V1) {
+        if (version != SYNTHETIC_PASSWORD_VERSION_V3
+                && version != SYNTHETIC_PASSWORD_VERSION_V2
+                && version != SYNTHETIC_PASSWORD_VERSION_V1) {
             throw new RuntimeException("Unknown blob version");
         }
         if (blob[1] != type) {
@@ -958,7 +982,7 @@
             Log.e(TAG, "Fail to decrypt SP for user " + userId);
             return null;
         }
-        AuthenticationToken result = new AuthenticationToken();
+        AuthenticationToken result = new AuthenticationToken(version);
         if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) {
             if (!loadEscrowData(result, userId)) {
                 Log.e(TAG, "User is not escrowable: " + userId);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c18a79f..93b6620 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -923,7 +923,7 @@
         @Override
         public void dispatchMediaKeyEvent(String packageName, boolean asSystemService,
                 KeyEvent keyEvent, boolean needWakeLock) {
-            if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
+            if (keyEvent == null || !KeyEvent.isMediaSessionKey(keyEvent.getKeyCode())) {
                 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
                 return;
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4f4b6bf..4bd8f45 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -64,6 +64,7 @@
     private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
     private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
     private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
+    private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
 
     static final int NTWK_BLOCKED_POWER = 0;
     static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -145,6 +146,13 @@
         }
     }
 
+    void appIdleWlChanged(int uid, boolean isWhitelisted) {
+        synchronized (mLock) {
+            if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted));
+            mEventsBuffer.appIdleWlChanged(uid, isWhitelisted);
+        }
+    }
+
     void paroleStateChanged(boolean paroleOn) {
         synchronized (mLock) {
             if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn));
@@ -259,6 +267,10 @@
         return "App idle state of uid " + uid + ": " + idle;
     }
 
+    private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) {
+        return "App idle whitelist state of uid " + uid + ": " + isWhitelisted;
+    }
+
     private static String getParoleStateChanged(boolean paroleOn) {
         return "Parole state: " + paroleOn;
     }
@@ -409,6 +421,17 @@
             data.timeStamp = System.currentTimeMillis();
         }
 
+        public void appIdleWlChanged(int uid, boolean isWhitelisted) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_APP_IDLE_WL_CHANGED;
+            data.ifield1 = uid;
+            data.bfield1 = isWhitelisted;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
         public void paroleStateChanged(boolean paroleOn) {
             final Data data = getNextSlot();
             if (data == null) return;
@@ -487,6 +510,8 @@
                     return getDeviceIdleModeEnabled(data.bfield1);
                 case EVENT_APP_IDLE_STATE_CHANGED:
                     return getAppIdleChangedLog(data.ifield1, data.bfield1);
+                case EVENT_APP_IDLE_WL_CHANGED:
+                    return getAppIdleWlChangedLog(data.ifield1, data.bfield1);
                 case EVENT_PAROLE_STATE_CHANGED:
                     return getParoleStateChanged(data.bfield1);
                 case EVENT_TEMP_POWER_SAVE_WL_CHANGED:
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 099671d..7f650ee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -105,6 +105,12 @@
     public abstract void onAdminDataAvailable();
 
     /**
+     * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may
+     * still be in effect.
+     */
+    public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist);
+
+    /**
      * Sets a list of packages which are restricted by admin from accessing metered data.
      *
      * @param packageNames the list of restricted packages.
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d799642..0d6dadf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -464,6 +464,10 @@
     @GuardedBy("mUidRulesFirstLock")
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
 
+    // "Power save mode" is the concept used in the DeviceIdleController that includes various
+    // features including Doze and Battery Saver. It include Battery Saver, but "power save mode"
+    // and "battery saver" are not equivalent.
+
     /**
      * UIDs that have been white-listed to always be able to have network access
      * in power save mode, except device idle (doze) still applies.
@@ -484,6 +488,13 @@
     private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
 
     /**
+     * UIDs that have been white-listed temporarily to be able to have network access despite being
+     * idle. Other power saving restrictions still apply.
+     */
+    @GuardedBy("mUidRulesFirstLock")
+    private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray();
+
+    /**
      * UIDs that have been initially white-listed by system to avoid restricted background.
      */
     @GuardedBy("mUidRulesFirstLock")
@@ -543,7 +554,7 @@
 
     final Handler mHandler;
     @VisibleForTesting
-    public final Handler mUidEventHandler;
+    final Handler mUidEventHandler;
 
     private final ServiceThread mUidEventThread;
 
@@ -1454,7 +1465,7 @@
     }
 
     @VisibleForTesting
-    public void updateNetworks() throws InterruptedException {
+    void updateNetworks() throws InterruptedException {
         updateNetworksInternal();
         final CountDownLatch latch = new CountDownLatch(1);
         mHandler.post(() -> {
@@ -1499,7 +1510,7 @@
      * @return cycleDay to use in the mobile NetworkPolicy.
      */
     @VisibleForTesting
-    public int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
+    int getCycleDayFromCarrierConfig(@Nullable PersistableBundle config,
             int fallbackCycleDay) {
         if (config == null) {
             return fallbackCycleDay;
@@ -1531,7 +1542,7 @@
      * @return warningBytes to use in the mobile NetworkPolicy.
      */
     @VisibleForTesting
-    public long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
+    long getWarningBytesFromCarrierConfig(@Nullable PersistableBundle config,
             long fallbackWarningBytes) {
         if (config == null) {
             return fallbackWarningBytes;
@@ -1564,7 +1575,7 @@
      * @return limitBytes to use in the mobile NetworkPolicy.
      */
     @VisibleForTesting
-    public long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
+    long getLimitBytesFromCarrierConfig(@Nullable PersistableBundle config,
             long fallbackLimitBytes) {
         if (config == null) {
             return fallbackLimitBytes;
@@ -2028,7 +2039,7 @@
     }
 
     @VisibleForTesting
-    public NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
+    NetworkPolicy buildDefaultMobilePolicy(int subId, String subscriberId) {
         final NetworkTemplate template = buildTemplateMobileAll(subscriberId);
         final RecurrenceRule cycleRule = NetworkPolicy
                 .buildRule(ZonedDateTime.now().getDayOfMonth(), ZoneId.systemDefault());
@@ -3372,6 +3383,20 @@
                     fout.decreaseIndent();
                 }
 
+                size = mAppIdleTempWhitelistAppIds.size();
+                if (size > 0) {
+                    fout.println("App idle whitelist app ids:");
+                    fout.increaseIndent();
+                    for (int i = 0; i < size; i++) {
+                        fout.print("UID=");
+                        fout.print(mAppIdleTempWhitelistAppIds.keyAt(i));
+                        fout.print(": ");
+                        fout.print(mAppIdleTempWhitelistAppIds.valueAt(i));
+                        fout.println();
+                    }
+                    fout.decreaseIndent();
+                }
+
                 size = mDefaultRestrictBackgroundWhitelistUids.size();
                 if (size > 0) {
                     fout.println("Default restrict background whitelist uids:");
@@ -3464,7 +3489,7 @@
     }
 
     @VisibleForTesting
-    public boolean isUidForeground(int uid) {
+    boolean isUidForeground(int uid) {
         synchronized (mUidRulesFirstLock) {
             return isUidStateForeground(
                     mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY));
@@ -3640,12 +3665,15 @@
     }
 
     /**
+     * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze
+     * mode, and app idle).
+     *
      * @param deviceIdleMode if true then we don't consider
      *        {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is
      *        whitelisted.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) {
+    private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) {
         final int appId = UserHandle.getAppId(uid);
         boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId)
                 || mPowerSaveWhitelistAppIds.get(appId);
@@ -3660,7 +3688,7 @@
     @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) {
         if (enabled) {
-            final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid,
+            final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid,
                     chain == FIREWALL_CHAIN_DOZABLE);
             if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) {
                 setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW);
@@ -3712,8 +3740,10 @@
             if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
                     && !isUidForegroundOnRestrictPowerUL(uid)) {
                 setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
+                if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
             } else {
                 setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+                if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT");
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
@@ -3896,7 +3926,61 @@
         return UserHandle.isApp(uid) && hasInternetPermissions(uid);
     }
 
-    private boolean isUidIdle(int uid) {
+    /**
+     * Set whether or not an app should be whitelisted for network access while in app idle. Other
+     * power saving restrictions may still apply.
+     */
+    @VisibleForTesting
+    void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mUidRulesFirstLock) {
+            if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) {
+                // No change.
+                return;
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mLogger.appIdleWlChanged(uid, shouldWhitelist);
+                if (shouldWhitelist) {
+                    mAppIdleTempWhitelistAppIds.put(uid, true);
+                } else {
+                    mAppIdleTempWhitelistAppIds.delete(uid);
+                }
+                updateRuleForAppIdleUL(uid);
+                updateRulesForPowerRestrictionsUL(uid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
+    /** Return the list of UIDs currently in the app idle whitelist. */
+    @VisibleForTesting
+    int[] getAppIdleWhitelist() {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mUidRulesFirstLock) {
+            final int len = mAppIdleTempWhitelistAppIds.size();
+            int[] uids = new int[len];
+            for (int i = 0; i < len; ++i) {
+                uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i);
+            }
+            return uids;
+        }
+    }
+
+    /** Returns if the UID is currently considered idle. */
+    @VisibleForTesting
+    boolean isUidIdle(int uid) {
+        synchronized (mUidRulesFirstLock) {
+            if (mAppIdleTempWhitelistAppIds.get(uid)) {
+                // UID is temporarily whitelisted.
+                return false;
+            }
+        }
+
         final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         final int userId = UserHandle.getUserId(uid);
 
@@ -3940,6 +4024,7 @@
         mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
+        mAppIdleTempWhitelistAppIds.delete(uid);
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -3984,7 +4069,7 @@
      * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're
      *     also blacklisted.
      * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}),
-     *     no UIDs other those whitelisted will have access.
+     *     no UIDs other than those whitelisted will have access.
      * <ul>
      *
      * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the
@@ -4194,7 +4279,7 @@
         final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode;
         final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
 
-        final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode);
+        final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
         final int oldRule = oldUidRules & MASK_ALL_NETWORKS;
         int newRule = RULE_NONE;
 
@@ -4761,13 +4846,13 @@
     }
 
     @VisibleForTesting
-    public void addIdleHandler(IdleHandler handler) {
+    void addIdleHandler(IdleHandler handler) {
         mHandler.getLooper().getQueue().addIdleHandler(handler);
     }
 
     @GuardedBy("mUidRulesFirstLock")
     @VisibleForTesting
-    public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
+    void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) {
         mRestrictBackgroundPowerState = result;
 
         boolean restrictBackground = result.batterySaverEnabled;
@@ -5023,6 +5108,11 @@
         }
 
         @Override
+        public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) {
+            NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist);
+        }
+
+        @Override
         public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
             setMeteredRestrictedPackagesInternal(packageNames, userId);
         }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 56d41c5..156c01d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -78,6 +78,8 @@
         pw.println("    Adds a UID to the whitelist for restrict background usage.");
         pw.println("  add restrict-background-blacklist UID");
         pw.println("    Adds a UID to the blacklist for restrict background usage.");
+        pw.println("  add app-idle-whitelist UID");
+        pw.println("    Adds a UID to the temporary app idle whitelist.");
         pw.println("  get restrict-background");
         pw.println("    Gets the global restrict background usage status.");
         pw.println("  list wifi-networks [true|false]");
@@ -92,6 +94,8 @@
         pw.println("    Removes a UID from the whitelist for restrict background usage.");
         pw.println("  remove restrict-background-blacklist UID");
         pw.println("    Removes a UID from the blacklist for restrict background usage.");
+        pw.println("  remove app-idle-whitelist UID");
+        pw.println("    Removes a UID from the temporary app idle whitelist.");
         pw.println("  set metered-network ID [undefined|true|false]");
         pw.println("    Toggles whether the given wi-fi network is metered.");
         pw.println("  set restrict-background BOOLEAN");
@@ -142,6 +146,8 @@
             return -1;
         }
         switch(type) {
+            case "app-idle-whitelist":
+                return listAppIdleWhitelist();
             case "wifi-networks":
                 return listWifiNetworks();
             case "restrict-background-whitelist":
@@ -165,6 +171,8 @@
                 return addRestrictBackgroundWhitelist();
             case "restrict-background-blacklist":
                 return addRestrictBackgroundBlacklist();
+            case "app-idle-whitelist":
+                return addAppIdleWhitelist();
         }
         pw.println("Error: unknown add type '" + type + "'");
         return -1;
@@ -182,14 +190,20 @@
                 return removeRestrictBackgroundWhitelist();
             case "restrict-background-blacklist":
                 return removeRestrictBackgroundBlacklist();
+            case "app-idle-whitelist":
+                return removeAppIdleWhitelist();
         }
         pw.println("Error: unknown remove type '" + type + "'");
         return -1;
     }
 
     private int listUidPolicies(String msg, int policy) throws RemoteException {
-        final PrintWriter pw = getOutPrintWriter();
         final int[] uids = mInterface.getUidsWithPolicy(policy);
+        return listUidList(msg, uids);
+    }
+
+    private int listUidList(String msg, int[] uids) {
+        final PrintWriter pw = getOutPrintWriter();
         pw.print(msg); pw.print(": ");
         if (uids.length == 0) {
             pw.println("none");
@@ -214,6 +228,12 @@
                 POLICY_REJECT_METERED_BACKGROUND);
     }
 
+    private int listAppIdleWhitelist() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final int[] uids = mInterface.getAppIdleWhitelist();
+        return listUidList("App Idle whitelisted UIDs", uids);
+    }
+
     private int getRestrictBackground() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         pw.print("Restrict background status: ");
@@ -277,6 +297,23 @@
         return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
     }
 
+    private int setAppIdleWhitelist(boolean isWhitelisted) {
+        final int uid = getUidFromNextArg();
+        if (uid < 0) {
+            return uid;
+        }
+        mInterface.setAppIdleWhitelist(uid, isWhitelisted);
+        return 0;
+    }
+
+    private int addAppIdleWhitelist() throws RemoteException {
+        return setAppIdleWhitelist(true);
+    }
+
+    private int removeAppIdleWhitelist() throws RemoteException {
+        return setAppIdleWhitelist(false);
+    }
+
     private int listWifiNetworks() {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 9222740..84bb13e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.app.Notification;
 import android.service.notification.NotificationStats;
 
 import com.android.internal.statusbar.NotificationVisibility;
@@ -26,7 +27,7 @@
     void onNotificationClick(int callingUid, int callingPid, String key,
             NotificationVisibility nv);
     void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex,
-            NotificationVisibility nv);
+            Notification.Action action, NotificationVisibility nv, boolean generatedByAssistant);
     void onNotificationClear(int callingUid, int callingPid,
             String pkg, String tag, int id, int userId, String key,
             @NotificationStats.DismissalSurface int dismissalSurface,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6005872..25a1626 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,6 +198,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -206,6 +207,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
+import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.lights.Light;
@@ -264,11 +266,12 @@
 
     // message codes
     static final int MESSAGE_DURATION_REACHED = 2;
-    static final int MESSAGE_SAVE_POLICY_FILE = 3;
+    // 3: removed to a different handler
     static final int MESSAGE_SEND_RANKING_UPDATE = 4;
     static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
     static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
     static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7;
+    static final int MESSAGE_ON_PACKAGE_CHANGED = 8;
 
     // ranking thread messages
     private static final int MESSAGE_RECONSIDER_RANKING = 1000;
@@ -425,6 +428,7 @@
     private GroupHelper mGroupHelper;
     private int mAutoGroupAtCount;
     private boolean mIsTelevision;
+    private boolean mIsAutomotive;
 
     private MetricsLogger mMetricsLogger;
     private Predicate<String> mAllowedManagedServicePackages;
@@ -570,7 +574,7 @@
             mListeners.migrateToXml();
             mAssistants.migrateToXml();
             mConditionProviders.migrateToXml();
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         mAssistants.ensureAssistant();
@@ -600,31 +604,28 @@
         }
     }
 
-    public void savePolicyFile() {
-        mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
-        mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
-    }
-
     private void handleSavePolicyFile() {
-        if (DBG) Slog.d(TAG, "handleSavePolicyFile");
-        synchronized (mPolicyFile) {
-            final FileOutputStream stream;
-            try {
-                stream = mPolicyFile.startWrite();
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to save policy file", e);
-                return;
-            }
+        IoThread.getHandler().post(() -> {
+            if (DBG) Slog.d(TAG, "handleSavePolicyFile");
+            synchronized (mPolicyFile) {
+                final FileOutputStream stream;
+                try {
+                    stream = mPolicyFile.startWrite();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to save policy file", e);
+                    return;
+                }
 
-            try {
-                writePolicyXml(stream, false /*forBackup*/);
-                mPolicyFile.finishWrite(stream);
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to save policy file, restoring backup", e);
-                mPolicyFile.failWrite(stream);
+                try {
+                    writePolicyXml(stream, false /*forBackup*/);
+                    mPolicyFile.finishWrite(stream);
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to save policy file, restoring backup", e);
+                    mPolicyFile.failWrite(stream);
+                }
             }
-        }
-        BackupManager.dataChanged(getContext().getPackageName());
+            BackupManager.dataChanged(getContext().getPackageName());
+        });
     }
 
     private void writePolicyXml(OutputStream stream, boolean forBackup) throws IOException {
@@ -752,7 +753,8 @@
 
         @Override
         public void onNotificationActionClick(int callingUid, int callingPid, String key,
-                int actionIndex, NotificationVisibility nv) {
+                int actionIndex, Notification.Action action, NotificationVisibility nv,
+                boolean generatedByAssistant) {
             exitIdle();
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
@@ -772,6 +774,8 @@
                         nv.rank, nv.count);
                 nv.recycle();
                 reportUserInteraction(r);
+                mAssistants.notifyAssistantActionClicked(
+                        r.sbn, actionIndex, action, generatedByAssistant);
             }
         }
 
@@ -1130,12 +1134,7 @@
                     }
                 }
 
-                mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
-                mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
-                mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
-                mPreferencesHelper.onPackagesChanged(
-                        removingPackage, changeUserId, pkgList, uidList);
-                savePolicyFile();
+                mHandler.scheduleOnPackageChanged(removingPackage, changeUserId, pkgList, uidList);
             }
         }
     };
@@ -1202,7 +1201,7 @@
                 mListeners.onUserRemoved(userId);
                 mConditionProviders.onUserRemoved(userId);
                 mAssistants.onUserRemoved(userId);
-                savePolicyFile();
+                handleSavePolicyFile();
             } else if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 mUserProfiles.updateCache(context);
@@ -1390,6 +1389,11 @@
     }
 
     @VisibleForTesting
+    void setIsAutomotive(boolean isAutomotive) {
+        mIsAutomotive = isAutomotive;
+    }
+
+    @VisibleForTesting
     void setIsTelevision(boolean isTelevision) {
         mIsTelevision = isTelevision;
     }
@@ -1453,7 +1457,7 @@
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
             public void onConfigChanged() {
-                savePolicyFile();
+                handleSavePolicyFile();
             }
 
             @Override
@@ -1545,6 +1549,9 @@
 
         mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
                 || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+
+        mIsAutomotive =
+                mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, 0);
     }
 
     @Override
@@ -1752,7 +1759,7 @@
                     modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
         }
 
-        savePolicyFile();
+        handleSavePolicyFile();
     }
 
     private void maybeNotifyChannelOwner(String pkg, int uid, NotificationChannel preUpdate,
@@ -2220,7 +2227,7 @@
                 Slog.w(TAG, "Can't notify app about app block change", e);
             }
 
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         /**
@@ -2277,7 +2284,7 @@
         public void setShowBadge(String pkg, int uid, boolean showBadge) {
             checkCallerIsSystem();
             mPreferencesHelper.setShowBadge(pkg, uid, showBadge);
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -2293,7 +2300,7 @@
                 if (info != null) {
                     mPreferencesHelper.setNotificationDelegate(
                             callingPkg, callingUid, delegate, info.uid);
-                    savePolicyFile();
+                    handleSavePolicyFile();
                 }
             } catch (RemoteException e) {
                 // :(
@@ -2304,7 +2311,7 @@
         public void revokeNotificationDelegate(String callingPkg) {
             checkCallerIsSameApp(callingPkg);
             mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -2339,7 +2346,7 @@
                 NotificationChannelGroup group) throws RemoteException {
             enforceSystemOrSystemUI("Caller not system or systemui");
             createNotificationChannelGroup(pkg, uid, group, false, false);
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -2352,7 +2359,7 @@
                 final NotificationChannelGroup group = groups.get(i);
                 createNotificationChannelGroup(pkg, Binder.getCallingUid(), group, true, false);
             }
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         private void createNotificationChannelsImpl(String pkg, int uid,
@@ -2370,7 +2377,7 @@
                         mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
                         NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
             }
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -2415,7 +2422,7 @@
                     UserHandle.getUserHandleForUid(callingUid),
                     mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
                     NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -2457,7 +2464,7 @@
                 mListeners.notifyNotificationChannelGroupChanged(
                         pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
                         NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-                savePolicyFile();
+                handleSavePolicyFile();
             }
         }
 
@@ -2590,7 +2597,7 @@
                         true, UserHandle.getCallingUserId(), packages, uids);
             }
 
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
 
@@ -3378,7 +3385,7 @@
                 final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
                 try {
                     readPolicyXml(bais, true /*forRestore*/);
-                    savePolicyFile();
+                    handleSavePolicyFile();
                 } catch (NumberFormatException | XmlPullParserException | IOException e) {
                     Slog.w(TAG, "applyRestore: error reading payload", e);
                 }
@@ -3419,7 +3426,7 @@
                                     .setPackage(pkg)
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
-                    savePolicyFile();
+                    handleSavePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3563,7 +3570,7 @@
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
 
-                    savePolicyFile();
+                    handleSavePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3589,7 +3596,7 @@
                                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
                             UserHandle.of(userId), null);
 
-                    savePolicyFile();
+                    handleSavePolicyFile();
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -3672,7 +3679,7 @@
             verifyPrivilegedListener(token, user, false);
             createNotificationChannelGroup(
                     pkg, getUidForPackageAndUser(pkg, user), group, false, true);
-            savePolicyFile();
+            handleSavePolicyFile();
         }
 
         @Override
@@ -3721,7 +3728,7 @@
             }
             if (allow != mLockScreenAllowSecureNotifications) {
                 mLockScreenAllowSecureNotifications = allow;
-                savePolicyFile();
+                handleSavePolicyFile();
             }
         }
 
@@ -4244,18 +4251,7 @@
 
         // Fix the notification as best we can.
         try {
-            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
-                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
-                    (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
-            Notification.addFieldsFromContext(ai, notification);
-
-            int canColorize = mPackageManagerClient.checkPermission(
-                    android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
-            if (canColorize == PERMISSION_GRANTED) {
-                notification.flags |= Notification.FLAG_CAN_COLORIZE;
-            } else {
-                notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
-            }
+            fixNotification(notification, pkg, userId);
 
         } catch (NameNotFoundException e) {
             Slog.e(TAG, "Cannot create a context for sending app", e);
@@ -4356,6 +4352,33 @@
         mHandler.post(new EnqueueNotificationRunnable(userId, r));
     }
 
+    @VisibleForTesting
+    protected void fixNotification(Notification notification, String pkg, int userId)
+            throws NameNotFoundException {
+        final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
+                pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
+                (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
+        Notification.addFieldsFromContext(ai, notification);
+
+        int canColorize = mPackageManagerClient.checkPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+        if (canColorize == PERMISSION_GRANTED) {
+            notification.flags |= Notification.FLAG_CAN_COLORIZE;
+        } else {
+            notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
+        }
+
+        if (ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
+            int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
+                    android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
+            if (fullscreenIntentPermission != PERMISSION_GRANTED) {
+                notification.fullScreenIntent = null;
+                Log.w(TAG, "Package " + pkg +
+                        ": Use of fullScreenIntent requires the USE_FULL_SCREEN_INTENT permission");
+            }
+        }
+    }
+
     private void doChannelWarningToast(CharSequence toastText) {
         Binder.withCleanCallingIdentity(() -> {
             final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
@@ -4464,7 +4487,7 @@
                 Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
             }
             mSnoozeHelper.update(userId, r);
-            savePolicyFile();
+            handleSavePolicyFile();
             return false;
         }
 
@@ -4595,7 +4618,7 @@
                 mSnoozeHelper.snooze(r, mDuration);
             }
             r.recordSnoozed();
-            savePolicyFile();
+            handleSavePolicyFile();
         }
     }
 
@@ -4673,7 +4696,7 @@
                     if (mReason != REASON_SNOOZED) {
                         final boolean wasSnoozed = mSnoozeHelper.cancel(mUserId, mPkg, mTag, mId);
                         if (wasSnoozed) {
-                            savePolicyFile();
+                            handleSavePolicyFile();
                         }
                     }
                 }
@@ -5117,7 +5140,9 @@
 
         // Should this notification make noise, vibe, or use the LED?
         final boolean aboveThreshold =
-                record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
+                mIsAutomotive
+                        ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
+                        : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
 
         // Remember if this notification already owns the notification channels.
         boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
@@ -5688,6 +5713,16 @@
         }
     }
 
+    private void handleOnPackageChanged(boolean removingPackage, int changeUserId,
+            String[] pkgList, int[] uidList) {
+        mListeners.onPackagesChanged(removingPackage, pkgList, uidList);
+        mAssistants.onPackagesChanged(removingPackage, pkgList, uidList);
+        mConditionProviders.onPackagesChanged(removingPackage, pkgList, uidList);
+        mPreferencesHelper.onPackagesChanged(
+                removingPackage, changeUserId, pkgList, uidList);
+        handleSavePolicyFile();
+    }
+
     protected class WorkerHandler extends Handler
     {
         public WorkerHandler(Looper looper) {
@@ -5700,13 +5735,10 @@
             switch (msg.what)
             {
                 case MESSAGE_DURATION_REACHED:
-                    handleDurationReached((ToastRecord)msg.obj);
+                    handleDurationReached((ToastRecord) msg.obj);
                     break;
                 case MESSAGE_FINISH_TOKEN_TIMEOUT:
-                    handleKillTokenTimeout((ToastRecord)msg.obj);
-                    break;
-                case MESSAGE_SAVE_POLICY_FILE:
-                    handleSavePolicyFile();
+                    handleKillTokenTimeout((ToastRecord) msg.obj);
                     break;
                 case MESSAGE_SEND_RANKING_UPDATE:
                     handleSendRankingUpdate();
@@ -5717,6 +5749,12 @@
                 case MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED:
                     handleListenerInterruptionFilterChanged(msg.arg1);
                     break;
+                case MESSAGE_ON_PACKAGE_CHANGED:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    handleOnPackageChanged((boolean) args.arg1, args.argi1, (String[]) args.arg2,
+                            (int[]) args.arg3);
+                    args.recycle();
+                    break;
             }
         }
 
@@ -5732,6 +5770,16 @@
                 sendMessage(Message.obtain(this, cancelRunnable));
             }
         }
+
+        protected void scheduleOnPackageChanged(boolean removingPackage, int changeUserId,
+                String[] pkgList, int[] uidList) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = removingPackage;
+            args.argi1 = changeUserId;
+            args.arg2 = pkgList;
+            args.arg3 = uidList;
+            sendMessage(Message.obtain(this, MESSAGE_ON_PACKAGE_CHANGED, args));
+        }
     }
 
     private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -6209,7 +6257,7 @@
             Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
         }
         mSnoozeHelper.repost(key);
-        savePolicyFile();
+        handleSavePolicyFile();
     }
 
     @GuardedBy("mNotificationLock")
@@ -6923,6 +6971,27 @@
                     });
         }
 
+        @GuardedBy("mNotificationLock")
+        void notifyAssistantActionClicked(
+                final StatusBarNotification sbn, int actionIndex, Notification.Action action,
+                boolean generatedByAssistant) {
+            final String key = sbn.getKey();
+            notifyAssistantLocked(
+                    sbn,
+                    false /* sameUserOnly */,
+                    (assistant, sbnHolder) -> {
+                        try {
+                            assistant.onActionClicked(
+                                    key,
+                                    action,
+                                    generatedByAssistant
+                                            ? NotificationAssistantService.SOURCE_FROM_ASSISTANT
+                                            : NotificationAssistantService.SOURCE_FROM_APP);
+                        } catch (RemoteException ex) {
+                            Log.e(TAG, "unable to notify assistant (snoozed): " + assistant, ex);
+                        }
+                    });
+        }
 
         /**
          * asynchronously notify the assistant that a notification has been snoozed until a
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f279af0..94d276c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -309,6 +309,7 @@
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
             populateZenRule(automaticZenRule, rule, true);
+            newConfig.automaticRules.put(rule.id, rule);
             if (setConfigLocked(newConfig, reason, rule.component, true)) {
                 return rule.id;
             } else {
diff --git a/services/core/java/com/android/server/oemlock/OemLock.java b/services/core/java/com/android/server/oemlock/OemLock.java
index ee70c29..352884b 100644
--- a/services/core/java/com/android/server/oemlock/OemLock.java
+++ b/services/core/java/com/android/server/oemlock/OemLock.java
@@ -19,6 +19,9 @@
 import android.annotation.Nullable;
 
 abstract class OemLock {
+    @Nullable
+    abstract String getLockName();
+
     abstract void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature);
     abstract boolean isOemUnlockAllowedByCarrier();
 
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index a6200bf..6e82c24 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -113,6 +113,19 @@
      */
     private final IBinder mService = new IOemLockService.Stub() {
         @Override
+        @Nullable
+        public String getLockName() {
+            enforceManageCarrierOemUnlockPermission();
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mOemLock.getLockName();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
             enforceManageCarrierOemUnlockPermission();
             enforceUserIsAdmin();
diff --git a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
index d9362d4..a1c27d6 100644
--- a/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
+++ b/services/core/java/com/android/server/oemlock/PersistentDataBlockLock.java
@@ -40,6 +40,12 @@
     }
 
     @Override
+    @Nullable
+    String getLockName() {
+        return "";
+    }
+
+    @Override
     void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
         // Note: this implementation does not require a signature
         if (signature != null) {
diff --git a/services/core/java/com/android/server/oemlock/VendorLock.java b/services/core/java/com/android/server/oemlock/VendorLock.java
index 1b9de36..37540d0 100644
--- a/services/core/java/com/android/server/oemlock/VendorLock.java
+++ b/services/core/java/com/android/server/oemlock/VendorLock.java
@@ -22,8 +22,6 @@
 import android.hardware.oemlock.V1_0.OemLockSecureStatus;
 import android.hardware.oemlock.V1_0.OemLockStatus;
 import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.Slog;
 
 import java.util.ArrayList;
@@ -55,14 +53,49 @@
     }
 
     @Override
+    @Nullable
+    String getLockName() {
+        final Integer[] requestStatus = new Integer[1];
+        final String[] lockName = new String[1];
+
+        try {
+            mOemLock.getName((status, name) -> {
+                requestStatus[0] = status;
+                lockName[0] = name;
+            });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to get name from HAL", e);
+            throw e.rethrowFromSystemServer();
+        }
+
+        switch (requestStatus[0]) {
+            case OemLockStatus.OK:
+                // Success
+                return lockName[0];
+
+            case OemLockStatus.FAILED:
+                Slog.e(TAG, "Failed to get OEM lock name.");
+                return null;
+
+            default:
+                Slog.e(TAG, "Unknown return value indicates code is out of sync with HAL");
+                return null;
+        }
+    }
+
+    @Override
     void setOemUnlockAllowedByCarrier(boolean allowed, @Nullable byte[] signature) {
         try {
-            switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, toByteArrayList(signature))) {
+            ArrayList<Byte> signatureBytes = toByteArrayList(signature);
+            switch (mOemLock.setOemUnlockAllowedByCarrier(allowed, signatureBytes)) {
                 case OemLockSecureStatus.OK:
                     Slog.i(TAG, "Updated carrier allows OEM lock state to: " + allowed);
                     return;
 
                 case OemLockSecureStatus.INVALID_SIGNATURE:
+                    if (signatureBytes.isEmpty()) {
+                        throw new IllegalArgumentException("Signature required for carrier unlock");
+                    }
                     throw new SecurityException(
                             "Invalid signature used in attempt to carrier unlock");
 
@@ -154,9 +187,9 @@
         }
     }
 
-    private ArrayList toByteArrayList(byte[] data) {
+    private ArrayList<Byte> toByteArrayList(byte[] data) {
         if (data == null) {
-            return null;
+            return new ArrayList<Byte>();
         }
         ArrayList<Byte> result = new ArrayList<Byte>(data.length);
         for (final byte b : data) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 731e6bc..6d59827 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -46,7 +46,7 @@
  * Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
  */
 class IdmapManager {
-    private static final boolean FEATURE_FLAG_IDMAP2 = false;
+    private static final boolean FEATURE_FLAG_IDMAP2 = true;
 
     private final Installer mInstaller;
     private IIdmap2 mIdmap2Service;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b490381..7f1fb6c 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -310,7 +310,7 @@
                             .setPackage(packageName),
                     user);
             if (Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 0) == 0) {
+                    Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
                 return launcherActivities;
             }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index cc640f0..093b85e 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -16,6 +16,28 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
+
+import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
+import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
+import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
+import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
+import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
+
+import static dalvik.system.DexFile.getSafeModeCompilerFilter;
+import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -41,6 +63,8 @@
 import com.android.server.pm.dex.DexoptUtils;
 import com.android.server.pm.dex.PackageDexUsage;
 
+import dalvik.system.DexFile;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -48,32 +72,6 @@
 import java.util.List;
 import java.util.Map;
 
-import dalvik.system.DexFile;
-
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
-
-import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
-import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
-import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
-import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
-import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
-import static com.android.server.pm.Installer.DEXOPT_FORCE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
-import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
-import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
-import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
-import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
-import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
-import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-
-import static com.android.server.pm.PackageManagerService.WATCHDOG_TIMEOUT;
-
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getReasonName;
-
-import static dalvik.system.DexFile.getSafeModeCompilerFilter;
-import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
-
 /**
  * Helper class for running dexopt command on packages.
  */
@@ -544,8 +542,7 @@
         // The flag isDexoptInstallWithDexMetadata applies only on installs when we know that
         // the user does not have an existing profile.
         boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
-        boolean isPublic = !info.isForwardLocked() &&
-                (!isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata());
+        boolean isPublic = !isProfileGuidedFilter || options.isDexoptInstallWithDexMetadata();
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1a5b86c..2e9a71a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -455,12 +455,6 @@
                         + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
             }
 
-            if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
-                    || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                throw new IllegalArgumentException(
-                        "New installs into ASEC containers no longer supported");
-            }
-
             // Defensively resize giant app icons
             if (params.appIcon != null) {
                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
@@ -487,21 +481,10 @@
                 if (!PackageHelper.fitsOnInternal(mContext, params)) {
                     throw new IOException("No suitable internal storage available");
                 }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
-                if (!PackageHelper.fitsOnExternal(mContext, params)) {
-                    throw new IOException("No suitable external storage available");
-                }
-
-            } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
-                // For now, installs to adopted media are treated as internal from
-                // an install flag point-of-view.
-                params.setInstallFlagsInternal();
-
             } else {
                 // For now, installs to adopted media are treated as internal from
                 // an install flag point-of-view.
-                params.setInstallFlagsInternal();
+                params.installFlags |= PackageManager.INSTALL_INTERNAL;
 
                 // Resolve best location for install, based on combination of
                 // requested install flags, delta size, and manifest settings.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index acbd81d..dab4e79 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -43,7 +43,6 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.INSTALL_EXTERNAL;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
@@ -54,12 +53,12 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
-import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -118,8 +117,7 @@
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
-import static com.android.server.pm.permission.PermissionsState
-        .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -199,6 +197,7 @@
 import android.content.pm.Signature;
 import android.content.pm.SuspendDialogInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.UsesPermissionInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
 import android.content.pm.VersionedPackage;
@@ -315,8 +314,7 @@
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
-        .DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
 import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
@@ -375,6 +373,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -646,9 +645,6 @@
     /** Directory where installed application's 32-bit native libraries are copied. */
     private static final File sAppLib32InstallDir =
             new File(Environment.getDataDirectory(), "app-lib");
-    /** Directory where code and non-resource assets of forward-locked applications are stored */
-    private static final File sDrmAppPrivateInstallDir =
-            new File(Environment.getDataDirectory(), "app-private");
 
     // ----------------------------------------------------------------
 
@@ -1811,12 +1807,10 @@
                             firstUserIds, firstInstantUserIds);
                 }
 
-                // Send broadcast package appeared if forward locked/external for all users
-                // treat asec-hosted packages like removable media on upgrade
-                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
+                // Send broadcast package appeared if external for all users
+                if (isExternal(res.pkg)) {
                     if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + res.pkg
-                                + " is ASEC-hosted -> AVAILABLE");
+                        Slog.i(TAG, "upgrading pkg " + res.pkg + " is external");
                     }
                     final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
                     ArrayList<String> pkgList = new ArrayList<>(1);
@@ -2629,10 +2623,6 @@
                         SystemClock.uptimeMillis());
                 scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
-                        | PackageParser.PARSE_FORWARD_LOCK,
-                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
-
                 // Remove disable package settings for updated system apps that were
                 // removed via an OTA. If the update is no longer present, remove the
                 // app completely. Otherwise, revoke their system privileges.
@@ -5407,7 +5397,7 @@
         synchronized (mPackages) {
             Signature[] s1;
             Signature[] s2;
-            Object obj = mSettings.getUserIdLPr(uid1);
+            Object obj = mSettings.getSettingLPr(uid1);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5426,7 +5416,7 @@
             } else {
                 return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
             }
-            obj = mSettings.getUserIdLPr(uid2);
+            obj = mSettings.getSettingLPr(uid2);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     if (isCallerInstantApp) {
@@ -5485,7 +5475,7 @@
         // reader
         synchronized (mPackages) {
             final PackageParser.SigningDetails signingDetails;
-            final Object obj = mSettings.getUserIdLPr(uid);
+            final Object obj = mSettings.getSettingLPr(uid);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
@@ -5598,7 +5588,7 @@
         uid = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getSettingLPr(uid);
             if (obj instanceof SharedUserSetting) {
                 if (isCallerInstantApp) {
                     return null;
@@ -5634,7 +5624,7 @@
             return null;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.name + ":" + sus.userId;
@@ -5662,7 +5652,7 @@
         synchronized (mPackages) {
             for (int i = uids.length - 1; i >= 0; i--) {
                 final int uid = uids[i];
-                Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+                Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
                 if (obj instanceof SharedUserSetting) {
                     final SharedUserSetting sus = (SharedUserSetting) obj;
                     names[i] = "shared:" + sus.name;
@@ -5711,7 +5701,7 @@
             return 0;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgFlags;
@@ -5733,7 +5723,7 @@
             return 0;
         }
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+            Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid));
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 return sus.pkgPrivateFlags;
@@ -5756,7 +5746,7 @@
         uid = UserHandle.getAppId(uid);
         // reader
         synchronized (mPackages) {
-            Object obj = mSettings.getUserIdLPr(uid);
+            Object obj = mSettings.getSettingLPr(uid);
             if (obj instanceof SharedUserSetting) {
                 final SharedUserSetting sus = (SharedUserSetting) obj;
                 final Iterator<PackageSetting> it = sus.packages.iterator();
@@ -6396,7 +6386,7 @@
                 callingUid = mIsolatedOwners.get(callingUid);
             }
             final int appId = UserHandle.getAppId(callingUid);
-            final Object obj = mSettings.getUserIdLPr(appId);
+            final Object obj = mSettings.getSettingLPr(appId);
             if (obj instanceof PackageSetting) {
                 final PackageSetting ps = (PackageSetting) obj;
                 final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
@@ -8631,7 +8621,7 @@
                     + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
 
             final InstallArgs args = createInstallArgsForExisting(
-                    packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                    pkgSetting.codePathString,
                     pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
             args.cleanUpResourcesLI();
             synchronized (mPackages) {
@@ -8704,7 +8694,7 @@
                         + "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
                         + "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
                 InstallArgs args = createInstallArgsForExisting(
-                        packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
+                        pkgSetting.codePathString,
                         pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -8726,7 +8716,13 @@
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
         if (scanResult.success) {
             synchronized (mPackages) {
-                commitScanResultLocked(scanResult);
+                try {
+                    prepareScanResultLocked(scanResult);
+                    commitScanResultLocked(scanResult);
+                } catch (PackageManagerException e) {
+                    unprepareScanResultLocked(scanResult);
+                    throw e;
+                }
             }
         }
 
@@ -10125,7 +10121,37 @@
                 }
             }
             for (ScanResult result : results) {
-                commitScanResultLocked(result);
+                try {
+                    prepareScanResultLocked(result);
+                    commitScanResultLocked(result);
+                } catch (PackageManagerException e) {
+                    unprepareScanResultLocked(result);
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /** Prepares the system to commit a {@link ScanResult} in a way that will not fail. */
+    private void prepareScanResultLocked(@NonNull ScanResult result)
+            throws PackageManagerException {
+        if (!result.existingSettingCopied) {
+            // THROWS: when we can't allocate a user id. add call to check if there's
+            // enough space to ensure we won't throw; otherwise, don't modify state
+            mSettings.registerAppIdLPw(result.pkgSetting);
+        }
+    }
+
+    /**
+     * Reverts any changes to the system that were made by
+     * {@link #prepareScanResultLocked(ScanResult)}
+     */
+    private void unprepareScanResultLocked(@NonNull ScanResult result) {
+        if (!result.existingSettingCopied) {
+            // iff we've acquired an app ID for a new package setting, remove it so that it can be
+            // acquired by another request.
+            if (result.pkgSetting.appId > 0) {
+                mSettings.removeAppIdLPw(result.pkgSetting.appId);
             }
         }
     }
@@ -10164,10 +10190,6 @@
             if (originalPkgSetting != null) {
                 mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
             }
-            // THROWS: when we can't allocate a user id. add call to check if there's
-            // enough space to ensure we won't throw; otherwise, don't modify state
-            mSettings.addUserToSettingLPw(pkgSetting);
-
             if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
                 mTransferedPackages.add(originalPkgSetting.name);
             }
@@ -10278,12 +10300,21 @@
                         compareSignatures(
                             signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
                             pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
-                        // Treat mismatched signatures on system packages using a shared UID as
-                        // fatal for the system overall, rather than just failing to install
-                        // whichever package happened to be scanned later.
-                        throw new IllegalStateException(
-                                "Signature mismatch on system package " + pkg.packageName
-                                + " for shared user " + pkgSetting.sharedUser);
+                        if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) {
+                            // Mismatched signatures is an error and silently skipping system
+                            // packages will likely break the device in unforeseen ways. However,
+                            // we allow the device to boot anyway because, prior to P, vendors were
+                            // not expecting the platform to crash in this situation.
+                            throw new PackageManagerException(
+                                    INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                    "Signature mismatch for shared user: " + pkgSetting.sharedUser);
+                        } else {
+                            // Treat mismatched signatures on system packages using a shared UID as
+                            // fatal for the system overall, rather than just failing to install
+                            // whichever package happened to be scanned later.
+                            throw new IllegalStateException("Signature mismatch on system package "
+                                + pkg.packageName + " for shared user " + pkgSetting.sharedUser);
+                        }
                     }
 
                     signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
@@ -11221,6 +11252,26 @@
                     }
                 }
             }
+
+            // Check permission usage info requirements.
+            if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {
+                for (UsesPermissionInfo upi : pkg.usesPermissionInfos) {
+                    if (!mPermissionManager.isPermissionUsageInfoRequired(upi.getPermission())) {
+                        continue;
+                    }
+                    if (upi.getDataSentOffDevice() == UsesPermissionInfo.USAGE_UNDEFINED
+                            || upi.getDataSharedWithThirdParty()
+                                == UsesPermissionInfo.USAGE_UNDEFINED
+                            || upi.getDataUsedForMonetization()
+                                == UsesPermissionInfo.USAGE_UNDEFINED
+                            || upi.getDataRetention() == UsesPermissionInfo.RETENTION_UNDEFINED) {
+                        // STOPSHIP: Make this throw
+                        Slog.wtf(TAG, "Package " + pkg.packageName + " does not provide usage "
+                                + "information for permission " + upi.getPermission()
+                                + ". This will be a fatal error in Q.");
+                    }
+                }
+            }
         }
     }
 
@@ -11536,11 +11587,8 @@
         // pass once we've determined ABI below.
         setNativeLibraryPaths(pkg, sAppLib32InstallDir);
 
-        // We would never need to extract libs for forward-locked and external packages,
-        // since the container service will do it for us. We shouldn't attempt to
-        // extract libs from system app when it was not updated.
-        if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() ||
-                (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) {
+        // We shouldn't attempt to extract libs from system app when it was not updated.
+        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
             extractLibs = false;
         }
 
@@ -11881,7 +11929,6 @@
         final String codePath = pkg.codePath;
         final File codeFile = new File(codePath);
         final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp();
-        final boolean asecApp = info.isForwardLocked() || info.isExternalAsec();
 
         info.nativeLibraryRootDir = null;
         info.nativeLibraryRootRequiresIsa = false;
@@ -11910,9 +11957,6 @@
                     info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot),
                             secondaryLibDir, apkName).getAbsolutePath();
                 }
-            } else if (asecApp) {
-                info.nativeLibraryRootDir = new File(codeFile.getParentFile(), LIB_DIR_NAME)
-                        .getAbsolutePath();
             } else {
                 final String apkName = deriveCodePathName(codePath);
                 info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName)
@@ -13496,7 +13540,7 @@
             }
 
             Signature[] callerSignature;
-            Object obj = mSettings.getUserIdLPr(callingUid);
+            Object obj = mSettings.getSettingLPr(callingUid);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
                     callerSignature =
@@ -14021,7 +14065,6 @@
         private int installLocationPolicy(PackageInfoLite pkgLite) {
             String packageName = pkgLite.packageName;
             int installLocation = pkgLite.installLocation;
-            boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             // reader
             synchronized (mPackages) {
                 // Currently installed package which the new package is attempting to replace or
@@ -14074,16 +14117,8 @@
                     if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                         // Check for updated system application.
                         if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            if (onSd) {
-                                Slog.w(TAG, "Cannot install update to system app on sdcard");
-                                return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
-                            }
                             return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                         } else {
-                            if (onSd) {
-                                // Install flag overrides everything.
-                                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-                            }
                             // If current upgrade specifies particular preference
                             if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                                 // Application explicitly specified internal.
@@ -14104,11 +14139,6 @@
                     }
                 }
             }
-            // All the special cases have been taken care of.
-            // Return result based on recommended install location.
-            if (onSd) {
-                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
-            }
             return pkgLite.recommendedInstallLocation;
         }
 
@@ -14125,70 +14155,59 @@
             if (origin.staged) {
                 if (origin.file != null) {
                     installFlags |= PackageManager.INSTALL_INTERNAL;
-                    installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                 } else {
                     throw new IllegalStateException("Invalid stage location");
                 }
             }
 
-            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
             final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
             PackageInfoLite pkgLite = null;
 
-            if (onInt && onSd) {
-                // Check if both bits are set.
-                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else if (onSd && ephemeral) {
-                Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
-                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
-            } else {
-                pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                        origin.resolvedPath, installFlags, packageAbiOverride);
 
-                if (DEBUG_INSTANT && ephemeral) {
-                    Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                    origin.resolvedPath, installFlags, packageAbiOverride);
+
+            if (DEBUG_INSTANT && ephemeral) {
+                Slog.v(TAG, "pkgLite for install: " + pkgLite);
+            }
+
+            /*
+             * If we have too little free space, try to free cache
+             * before giving up.
+             */
+            if (!origin.staged && pkgLite.recommendedInstallLocation
+                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
+                // TODO: focus freeing disk space on the target device
+                final StorageManager storage = StorageManager.from(mContext);
+                final long lowThreshold = storage.getStorageLowBytes(
+                        Environment.getDataDirectory());
+
+                final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
+                        origin.resolvedPath, packageAbiOverride);
+                if (sizeBytes >= 0) {
+                    try {
+                        mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
+                        pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+                                origin.resolvedPath, installFlags, packageAbiOverride);
+                    } catch (InstallerException e) {
+                        Slog.w(TAG, "Failed to free cache", e);
+                    }
                 }
 
                 /*
-                 * If we have too little free space, try to free cache
-                 * before giving up.
+                 * The cache free must have deleted the file we downloaded to install.
+                 *
+                 * TODO: fix the "freeCache" call to not delete the file we care about.
                  */
-                if (!origin.staged && pkgLite.recommendedInstallLocation
-                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
-                    // TODO: focus freeing disk space on the target device
-                    final StorageManager storage = StorageManager.from(mContext);
-                    final long lowThreshold = storage.getStorageLowBytes(
-                            Environment.getDataDirectory());
-
-                    final long sizeBytes = PackageManagerServiceUtils.calculateInstalledSize(
-                            origin.resolvedPath, packageAbiOverride);
-                    if (sizeBytes >= 0) {
-                        try {
-                            mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0);
-                            pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
-                                    origin.resolvedPath, installFlags, packageAbiOverride);
-                        } catch (InstallerException e) {
-                            Slog.w(TAG, "Failed to free cache", e);
-                        }
-                    }
-
-                    /*
-                     * The cache free must have deleted the file we
-                     * downloaded to install.
-                     *
-                     * TODO: fix the "freeCache" call to not delete
-                     *       the file we care about.
-                     */
-                    if (pkgLite.recommendedInstallLocation
-                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
-                        pkgLite.recommendedInstallLocation
+                if (pkgLite.recommendedInstallLocation
+                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
+                    pkgLite.recommendedInstallLocation
                             = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
-                    }
                 }
             }
 
+
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 int loc = pkgLite.recommendedInstallLocation;
                 if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
@@ -14208,24 +14227,21 @@
                     loc = installLocationPolicy(pkgLite);
                     if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                         ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-                    } else if (!onSd && !onInt) {
+                    } else if (!onInt) {
                         // Override install location with flags
                         if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                             // Set the flag to install on external media.
-                            installFlags |= PackageManager.INSTALL_EXTERNAL;
                             installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
                             if (DEBUG_INSTANT) {
                                 Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
                             }
                             installFlags |= PackageManager.INSTALL_INSTANT_APP;
-                            installFlags &= ~(PackageManager.INSTALL_EXTERNAL
-                                    |PackageManager.INSTALL_INTERNAL);
+                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                         } else {
                             // Make sure the flag for installing on external
                             // media is unset
                             installFlags |= PackageManager.INSTALL_INTERNAL;
-                            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                         }
                     }
                 }
@@ -14409,7 +14425,7 @@
      * Create args that describe an existing installed package. Typically used
      * when cleaning up old installs, or used as a move source.
      */
-    private InstallArgs createInstallArgsForExisting(int installFlags, String codePath,
+    private InstallArgs createInstallArgsForExisting(String codePath,
             String resourcePath, String[] instructionSets) {
         return new FileInstallArgs(codePath, resourcePath, instructionSets);
     }
@@ -14501,14 +14517,6 @@
             return PackageManager.INSTALL_SUCCEEDED;
         }
 
-        protected boolean isFwdLocked() {
-            return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
-        }
-
-        protected boolean isExternalAsec() {
-            return (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
-        }
-
         protected boolean isEphemeral() {
             return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
         }
@@ -14536,7 +14544,7 @@
     }
 
     /**
-     * Logic to handle installation of non-ASEC applications, including copying
+     * Logic to handle installation of new applications, including copying
      * and renaming logic.
      */
     class FileInstallArgs extends InstallArgs {
@@ -14558,9 +14566,6 @@
                     params.grantedRuntimePermissions,
                     params.traceMethod, params.traceCookie, params.signingDetails,
                     params.installReason, params.mParentInstallParams);
-            if (isFwdLocked()) {
-                throw new IllegalArgumentException("Forward locking only supported in ASEC");
-            }
         }
 
         /** Existing install */
@@ -15215,8 +15220,11 @@
         }
     }
     private static class ReconcileFailure extends PackageManagerException {
-        public ReconcileFailure(String message) {
-            super("Invalid reconcile request: " + message);
+        ReconcileFailure(String message) {
+            super("Reconcile failed: " + message);
+        }
+        ReconcileFailure(int reason, String message) {
+            super(reason, "Reconcile failed: " + message);
         }
     }
 
@@ -15235,10 +15243,12 @@
         @PackageManager.InstallFlags
         public final int installFlags;
         public final InstallArgs installArgs;
+        public final DeletePackageAction deletePackageAction;
 
         private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting,
                 UserHandle installForUser, PackageInstalledInfo installResult, int installFlags,
-                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) {
+                String volumeUuid, PrepareResult prepareResult, ScanResult scanResult,
+                DeletePackageAction deletePackageAction) {
             this.installArgs = installArgs;
             this.pkgSetting = pkgSetting;
             this.installForUser = installForUser;
@@ -15247,6 +15257,7 @@
             this.volumeUuid = volumeUuid;
             this.prepareResult = prepareResult;
             this.scanResult = scanResult;
+            this.deletePackageAction = deletePackageAction;
         }
     }
 
@@ -15259,14 +15270,30 @@
             final ScanResult scanResult = request.scannedPackages.get(installPackageName);
             final InstallArgs installArgs = request.installArgs.get(installPackageName);
             final PackageInstalledInfo res = request.installResults.get(installPackageName);
+            final PrepareResult prepareResult = request.preparedPackages.get(installPackageName);
             if (scanResult == null || installArgs == null || res == null) {
                 throw new ReconcileFailure(
                         "inputs not balanced; missing argument for " + installPackageName);
             }
+            final DeletePackageAction deletePackageAction;
+            // we only want to try to delete for non system apps
+            if (prepareResult.replace && !prepareResult.system) {
+                deletePackageAction = mayDeletePackageLocked(res.removedInfo,
+                        prepareResult.originalPs, prepareResult.disabledPs,
+                        prepareResult.childPackageSettings);
+                if (deletePackageAction == null) {
+                    throw new ReconcileFailure(
+                            PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE,
+                            "May not delete " + installPackageName + " to replace");
+                }
+            } else {
+                deletePackageAction = null;
+            }
             result.put(installPackageName,
                     new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(),
                             res, installArgs.installFlags, installArgs.volumeUuid,
-                            request.preparedPackages.get(installPackageName), scanResult));
+                            request.preparedPackages.get(installPackageName), scanResult,
+                            deletePackageAction));
         }
         return result;
     }
@@ -15291,7 +15318,7 @@
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
                         // installed.  We need to make sure to delete the older one's .apk.
-                        res.removedInfo.args = createInstallArgsForExisting(0,
+                        res.removedInfo.args = createInstallArgsForExisting(
                                 oldPackage.applicationInfo.getCodePath(),
                                 oldPackage.applicationInfo.getResourcePath(),
                                 getAppDexInstructionSets(oldPackage.applicationInfo));
@@ -15338,29 +15365,29 @@
                     final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0;
                     final int deleteFlags = PackageManager.DELETE_KEEP_DATA
                             | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP);
-                    // First delete the existing package while retaining the data directory
-                    if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags,
-                            res.removedInfo, true, pkg)) {
-                        // If the existing package wasn't successfully deleted
-                        res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE,
-                                "replaceNonSystemPackageLI");
-                        return false;
-                    } else {
-                        // Successfully deleted the old package; proceed with replace.
-
-                        // If deleted package lived in a container, give users a chance to
-                        // relinquish resources before killing.
-                        if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
-                            if (DEBUG_INSTALL) {
-                                Slog.i(TAG, "upgrading pkg " + oldPackage
-                                        + " is ASEC-hosted -> UNAVAILABLE");
-                            }
-                            final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
-                            final ArrayList<String> pkgList = new ArrayList<>(1);
-                            pkgList.add(oldPackage.applicationInfo.packageName);
-                            sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+                    try {
+                        executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName,
+                                null, true, request.mAllUsers, deleteFlags, true, pkg);
+                    } catch (SystemDeleteException e) {
+                        if (Build.IS_ENG) {
+                            throw new RuntimeException("Unexpected failure", e);
+                            // ignore; not possible for non-system app
                         }
                     }
+                    // Successfully deleted the old package; proceed with replace.
+
+                    // If deleted package lived in a container, give users a chance to
+                    // relinquish resources before killing.
+                    if (oldPackage.isForwardLocked() || isExternal(oldPackage)) {
+                        if (DEBUG_INSTALL) {
+                            Slog.i(TAG, "upgrading pkg " + oldPackage
+                                    + " is ASEC-hosted -> UNAVAILABLE");
+                        }
+                        final int[] uidArray = new int[]{oldPackage.applicationInfo.uid};
+                        final ArrayList<String> pkgList = new ArrayList<>(1);
+                        pkgList.add(oldPackage.applicationInfo.packageName);
+                        sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+                    }
 
                     // Update the in-memory copy of the previous code paths.
                     PackageSetting ps1 = mSettings.mPackages.get(
@@ -15414,8 +15441,10 @@
 
 
             try {
+                prepareScanResultLocked(scanResult);
                 commitScanResultLocked(scanResult);
             } catch (PackageManagerException e) {
+                unprepareScanResultLocked(scanResult);
                 res.setReturnCode(INSTALL_FAILED_INTERNAL_ERROR);
                 res.setError("Package couldn't be installed in " + pkg.codePath, e);
                 return false;
@@ -15570,8 +15599,6 @@
      */
     private void executePostCommitSteps(CommitRequest commitRequest) {
         for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
-            final boolean forwardLocked =
-                    ((reconciledPkg.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
             final boolean instantApp =
                     ((reconciledPkg.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
             final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
@@ -15604,10 +15631,8 @@
             //     This update happens in place!
             //
             // We only need to dexopt if the package meets ALL of the following conditions:
-            //   1) it is not forward locked.
-            //   2) it is not on on an external ASEC container.
-            //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
-            //   4) it is not debuggable.
+            //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
+            //   2) it is not debuggable.
             //
             // Note that we do not dexopt instant apps by default. dexopt can take some time to
             // complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15615,9 +15640,8 @@
             // continuous progress to the useur instead of mysteriously blocking somewhere in the
             // middle of running an instant app. The default behaviour can be overridden
             // via gservices.
-            final boolean performDexopt = !forwardLocked
-                    && !pkg.applicationInfo.isExternalAsec()
-                    && (!instantApp || Global.getInt(mContext.getContentResolver(),
+            final boolean performDexopt =
+                    (!instantApp || Global.getInt(mContext.getContentResolver(),
                     Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                     && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
 
@@ -15712,7 +15736,6 @@
                     // Parse old package
                     boolean oldExternal = isExternal(oldPackage);
                     int oldParseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                            | (oldPackage.isForwardLocked() ? PackageParser.PARSE_FORWARD_LOCK : 0)
                             | (oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
                     int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
                     try {
@@ -15772,13 +15795,16 @@
         @Nullable
         public final String renamedPackage;
         public final PackageFreezer freezer;
+        public final PackageSetting originalPs;
+        public final PackageSetting disabledPs;
+        public final PackageSetting[] childPackageSettings;
 
         private PrepareResult(int installReason, String volumeUuid,
                 String installerPackageName, UserHandle user, boolean replace, int scanFlags,
                 int parseFlags, PackageParser.Package existingPackage,
                 PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
-                String renamedPackage,
-                PackageFreezer freezer) {
+                String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+                PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
             this.installReason = installReason;
             this.volumeUuid = volumeUuid;
             this.installerPackageName = installerPackageName;
@@ -15792,6 +15818,9 @@
             this.system = system;
             this.renamedPackage = renamedPackage;
             this.freezer = freezer;
+            this.originalPs = originalPs;
+            this.disabledPs = disabledPs;
+            this.childPackageSettings = childPackageSettings;
         }
     }
 
@@ -15830,9 +15859,7 @@
         final String installerPackageName = args.installerPackageName;
         final String volumeUuid = args.volumeUuid;
         final File tmpPackageFile = new File(args.getCodePath());
-        final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
-        final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
-                || (args.volumeUuid != null));
+        final boolean onExternal = args.volumeUuid != null;
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
         final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
         final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
@@ -15859,16 +15886,14 @@
         if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
 
         // Sanity check
-        if (instantApp && (forwardLocked || onExternal)) {
-            Slog.i(TAG, "Incompatible ephemeral install; fwdLocked=" + forwardLocked
-                    + " external=" + onExternal);
+        if (instantApp && onExternal) {
+            Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
             throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
         }
 
         // Retrieve PackageSettings and parse package
         @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE
-                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0)
                 | (forceSdk ? PackageParser.PARSE_FORCE_SDK : 0);
 
@@ -16213,7 +16238,7 @@
                 pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
             }
 
-        } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
+        } else {
             // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
             scanFlags |= SCAN_NO_DEX;
 
@@ -16299,7 +16324,9 @@
             String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
             int targetParseFlags = parseFlags;
-
+            final PackageSetting ps;
+            final PackageSetting disabledPs;
+            final PackageSetting[] childPackages;
             if (replace) {
                 targetVolumeUuid = null;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
@@ -16319,7 +16346,6 @@
                 final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
 
                 final PackageParser.Package oldPackage;
-                final PackageSetting ps;
                 final String pkgName11 = pkg.packageName;
                 final int[] allUsers;
                 final int[] installedUsers;
@@ -16346,6 +16372,7 @@
                     }
 
                     ps = mSettings.mPackages.get(pkgName11);
+                    disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
 
                     // verify signatures are valid
                     final KeySetManagerService ksms = mSettings.mKeySetManagerService;
@@ -16442,49 +16469,52 @@
                     res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId));
                 }
 
-                final int childCount = (oldPackage.childPackages != null)
-                        ? oldPackage.childPackages.size() : 0;
-                for (int i = 0; i < childCount; i++) {
-                    boolean childPackageUpdated = false;
-                    PackageParser.Package childPkg = oldPackage.childPackages.get(i);
-                    final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName);
-                    if (res.addedChildPackages != null) {
-                        PackageInstalledInfo childRes = res.addedChildPackages.get(
-                                childPkg.packageName);
-                        if (childRes != null) {
-                            childRes.removedInfo.uid = childPkg.applicationInfo.uid;
-                            childRes.removedInfo.removedPackage = childPkg.packageName;
-                            if (childPs != null) {
-                                childRes.removedInfo.installerPackageName =
-                                        childPs.installerPackageName;
-                            }
-                            childRes.removedInfo.isUpdate = true;
-                            childRes.removedInfo.installReasons = res.removedInfo.installReasons;
-                            childPackageUpdated = true;
-                        }
-                    }
-                    if (!childPackageUpdated) {
-                        PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
-                        childRemovedRes.removedPackage = childPkg.packageName;
-                        if (childPs != null) {
-                            childRemovedRes.installerPackageName = childPs.installerPackageName;
-                        }
-                        childRemovedRes.isUpdate = false;
-                        childRemovedRes.dataRemoved = true;
-                        synchronized (mPackages) {
-                            if (childPs != null) {
-                                childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers,
-                                        true);
+                childPackages = mSettings.getChildSettingsLPr(ps);
+                if (childPackages != null) {
+                    for (PackageSetting childPs : childPackages) {
+                        boolean childPackageUpdated = false;
+                        PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg;
+                        if (res.addedChildPackages != null) {
+                            PackageInstalledInfo childRes = res.addedChildPackages.get(
+                                    childPkg.packageName);
+                            if (childRes != null) {
+                                childRes.removedInfo.uid = childPkg.applicationInfo.uid;
+                                childRes.removedInfo.removedPackage = childPkg.packageName;
+                                if (childPs != null) {
+                                    childRes.removedInfo.installerPackageName =
+                                            childPs.installerPackageName;
+                                }
+                                childRes.removedInfo.isUpdate = true;
+                                childRes.removedInfo.installReasons =
+                                        res.removedInfo.installReasons;
+                                childPackageUpdated = true;
                             }
                         }
-                        if (res.removedInfo.removedChildPackages == null) {
-                            res.removedInfo.removedChildPackages = new ArrayMap<>();
+                        if (!childPackageUpdated) {
+                            PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
+                            childRemovedRes.removedPackage = childPkg.packageName;
+                            if (childPs != null) {
+                                childRemovedRes.installerPackageName = childPs.installerPackageName;
+                            }
+                            childRemovedRes.isUpdate = false;
+                            childRemovedRes.dataRemoved = true;
+                            synchronized (mPackages) {
+                                if (childPs != null) {
+                                    childRemovedRes.origUsers = childPs.queryInstalledUsers(
+                                            allUsers,
+                                            true);
+                                }
+                            }
+                            if (res.removedInfo.removedChildPackages == null) {
+                                res.removedInfo.removedChildPackages = new ArrayMap<>();
+                            }
+                            res.removedInfo.removedChildPackages.put(childPkg.packageName,
+                                    childRemovedRes);
                         }
-                        res.removedInfo.removedChildPackages.put(childPkg.packageName,
-                                childRemovedRes);
                     }
                 }
 
+
                 sysPkg = (isSystemApp(oldPackage));
                 if (sysPkg) {
                     // Set the system/privileged/oem/vendor/product flags as needed
@@ -16527,6 +16557,9 @@
 
                 }
             } else { // new package install
+                ps = null;
+                childPackages = null;
+                disabledPs = null;
                 replace = false;
                 existingPackage = null;
                 // Remember this for later, in case we need to rollback this install
@@ -16557,9 +16590,11 @@
             }
             // we're passing the freezer back to be closed in a later phase of install
             shouldCloseFreezerBeforeReturn = false;
+
             return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
                     args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer);
+                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+                    ps, disabledPs, childPackages);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
@@ -16745,19 +16780,6 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
 
-    private int packageFlagsToInstallFlags(PackageSetting ps) {
-        int installFlags = 0;
-        if (isExternal(ps) && TextUtils.isEmpty(ps.volumeUuid)) {
-            // This existing package was an external ASEC install when we have
-            // the external flag without a UUID
-            installFlags |= PackageManager.INSTALL_EXTERNAL;
-        }
-        if (ps.isForwardLocked()) {
-            installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-        }
-        return installFlags;
-    }
-
     private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) {
         if (isExternal(pkg)) {
             if (TextUtils.isEmpty(pkg.volumeUuid)) {
@@ -16773,9 +16795,6 @@
     private void deleteTempPackageFiles() {
         final FilenameFilter filter =
                 (dir, name) -> name.startsWith("vmdl") && name.endsWith(".tmp");
-        for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
-            file.delete();
-        }
     }
 
     @Override
@@ -17525,34 +17544,20 @@
     /*
      * Tries to delete system package.
      */
-    private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg,
-            PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
-            boolean writeSettings) {
-        if (deletedPs.parentPackageName != null) {
-            Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
-            return false;
-        }
-
+    private void deleteSystemPackageLIF(DeletePackageAction action,
+            PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles,
+            int flags, PackageRemovedInfo outInfo, boolean writeSettings)
+            throws SystemDeleteException {
         final boolean applyUserRestrictions
                 = (allUserHandles != null) && (outInfo.origUsers != null);
-        final PackageSetting disabledPs;
         // Confirm if the system package has been updated
         // An updated system app can be deleted. This will also have to restore
         // the system pkg from system partition
         // reader
-        synchronized (mPackages) {
-            disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
-        }
-
+        final PackageSetting disabledPs = action.disabledPs;
         if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
                 + " disabledPs=" + disabledPs);
-
-        if (disabledPs == null) {
-            Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName);
-            return false;
-        } else if (DEBUG_REMOVE) {
-            Slog.d(TAG, "Deleting system pkg from data partition");
-        }
+        Slog.d(TAG, "Deleting system pkg from data partition");
 
         if (DEBUG_REMOVE) {
             if (applyUserRestrictions) {
@@ -17590,11 +17595,8 @@
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
 
-        boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
+        deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles,
                 outInfo, writeSettings, disabledPs.pkg);
-        if (!ret) {
-            return false;
-        }
 
         // writer
         synchronized (mPackages) {
@@ -17611,25 +17613,25 @@
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
-            installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles,
+            installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
                     outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
                     + e.getMessage());
-            return false;
+            // TODO(patb): can we avoid this; throw would come from scan...
+            throw new SystemDeleteException(e);
         } finally {
             if (disabledPs.pkg.isStub) {
                 mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
             }
         }
-        return true;
     }
 
     /**
      * Installs a package that's already on the system partition.
      */
     private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString,
-            boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+            @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
             @Nullable PermissionsState origPermissionState, boolean writeSettings)
                     throws PackageManagerException {
         @ParseFlags int parseFlags =
@@ -17637,7 +17639,7 @@
                 | PackageParser.PARSE_MUST_BE_APK
                 | PackageParser.PARSE_IS_SYSTEM_DIR;
         @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
-        if (isPrivileged || locationIsPrivileged(codePathString)) {
+        if (locationIsPrivileged(codePathString)) {
             scanFlags |= SCAN_AS_PRIVILEGED;
         }
         if (locationIsOem(codePathString)) {
@@ -17713,7 +17715,7 @@
         return pkg;
     }
 
-    private boolean deleteInstalledPackageLIF(PackageSetting ps,
+    private void deleteInstalledPackageLIF(PackageSetting ps,
             boolean deleteCodeAndResources, int flags, int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
@@ -17728,9 +17730,6 @@
                 for (int i = 0; i < childCount; i++) {
                     String childPackageName = ps.childPackageNames.get(i);
                     PackageSetting childPs = mSettings.mPackages.get(childPackageName);
-                    if (childPs == null) {
-                        return false;
-                    }
                     PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
                             childPackageName);
                     if (childInfo != null) {
@@ -17766,13 +17765,11 @@
         // Delete application code and resources only for parent packages
         if (ps.parentPackageName == null) {
             if (deleteCodeAndResources && (outInfo != null)) {
-                outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
+                outInfo.args = createInstallArgsForExisting(
                         ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                 if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
             }
         }
-
-        return true;
     }
 
     @Override
@@ -17828,26 +17825,59 @@
 
     private static class DeletePackageAction {
         public final PackageSetting deletingPs;
+        public final PackageSetting disabledPs;
+        public final PackageRemovedInfo outInfo;
 
-        private DeletePackageAction(PackageSetting deletingPs) {
+        private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs,
+                PackageRemovedInfo outInfo) {
             this.deletingPs = deletingPs;
+            this.disabledPs = disabledPs;
+            this.outInfo = outInfo;
         }
     }
 
     /**
-     * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null}
-     * otherwise.
+     * @return a {@link DeletePackageAction} if the provided package and related state may be
+     * deleted, {@code null} otherwise.
      */
     @Nullable
-    private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) {
-        synchronized (mPackages) {
-            final PackageSetting ps;
-            ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
+    @GuardedBy("mPackages")
+    private static DeletePackageAction mayDeletePackageLocked(
+            PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs,
+            @Nullable PackageSetting[] children) {
+        if (ps == null) {
+            return null;
+        }
+        if (isSystemApp(ps)) {
+            if (ps.parentPackageName != null) {
+                Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName);
                 return null;
             }
-            return new DeletePackageAction(ps);
+
+            // Confirm if the system package has been updated
+            // An updated system app can be deleted. This will also have to restore
+            // the system pkg from system partition
+            // reader
+            if (disabledPs == null) {
+                Slog.w(TAG,
+                        "Attempt to delete unknown system package " + ps.pkg.packageName);
+                return null;
+            }
         }
+        final int parentReferenceCount =
+                (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
+        final int childCount = children != null ? children.length : 0;
+        if (childCount != parentReferenceCount) {
+            return null;
+        }
+        if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) {
+            for (PackageSetting child : children) {
+                if (child == null || !ps.childPackageNames.contains(child.name)) {
+                    return null;
+                }
+            }
+        }
+        return new DeletePackageAction(ps, disabledPs, outInfo);
     }
 
     /*
@@ -17857,22 +17887,43 @@
             boolean deleteCodeAndResources, int[] allUserHandles, int flags,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
-        final DeletePackageAction action = mayDeletePackageLIF(packageName);
+        final DeletePackageAction action;
+        synchronized (mPackages) {
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps);
+            PackageSetting[] children = mSettings.getChildSettingsLPr(ps);
+            action = mayDeletePackageLocked(outInfo, ps, disabledPs, children);
+        }
         if (null == action) {
             return false;
         }
 
         if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user);
 
-        return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
-                allUserHandles, flags, outInfo, writeSettings, replacingPackage);
+        try {
+            executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources,
+                    allUserHandles, flags, writeSettings, replacingPackage);
+        } catch (SystemDeleteException e) {
+            return false;
+        }
+        return true;
     }
 
-    private boolean executeDeletePackageLIF(DeletePackageAction action,
+    private static class SystemDeleteException extends Exception {
+        public final PackageManagerException reason;
+
+        private SystemDeleteException(PackageManagerException reason) {
+            this.reason = reason;
+        }
+    }
+
+    /** Deletes a package. Only throws when install of a disabled package fails. */
+    private void executeDeletePackageLIF(DeletePackageAction action,
             String packageName, UserHandle user, boolean deleteCodeAndResources,
-            int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
-            boolean writeSettings, PackageParser.Package replacingPackage) {
+            int[] allUserHandles, int flags, boolean writeSettings,
+            PackageParser.Package replacingPackage) throws SystemDeleteException {
         final PackageSetting ps = action.deletingPs;
+        final PackageRemovedInfo outInfo = action.outInfo;
         final boolean systemApp = isSystemApp(ps);
         synchronized (mPackages) {
 
@@ -17888,7 +17939,7 @@
                 clearPackageStateForUserLIF(ps, removedUserId, outInfo);
                 markPackageUninstalledForUserLPw(ps, user);
                 scheduleWritePackageRestrictionsLocked(user);
-                return true;
+                return;
             }
         }
 
@@ -17917,7 +17968,7 @@
                     if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users");
                     clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                     scheduleWritePackageRestrictionsLocked(user);
-                    return true;
+                    return;
                 } else {
                     // We need to set it back to 'installed' so the uninstall
                     // broadcasts will be sent correctly.
@@ -17933,7 +17984,7 @@
                 if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app");
                 clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo);
                 scheduleWritePackageRestrictionsLocked(user);
-                return true;
+                return;
             }
         }
 
@@ -17958,15 +18009,15 @@
         }
 
         // TODO(b/109941548): break reasons for ret = false out into mayDelete method
-        final boolean ret;
         if (systemApp) {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
-            ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
+            deleteSystemPackageLIF(
+                    action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
-            ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
+            deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
                     outInfo, writeSettings, replacingPackage);
         }
 
@@ -18015,8 +18066,6 @@
                 }
             }
         }
-
-        return ret;
     }
 
     @GuardedBy("mPackages")
@@ -18477,7 +18526,7 @@
 
     @GuardedBy("mPackages")
     private int getUidTargetSdkVersionLockedLPr(int uid) {
-        Object obj = mSettings.getUserIdLPr(uid);
+        Object obj = mSettings.getSettingLPr(uid);
         if (obj instanceof SharedUserSetting) {
             final SharedUserSetting sus = (SharedUserSetting) obj;
             int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -19742,7 +19791,7 @@
                                 enableSystemPackageLPw(deletedPkg);
                             }
                             installPackageFromSystemLIF(deletedPkg.codePath,
-                                    false /*isPrivileged*/, null /*allUserHandles*/,
+                                    /*isPrivileged*/ null /*allUserHandles*/,
                                     null /*origUserHandles*/, null /*origPermissionsState*/,
                                     true /*writeSettings*/);
                         } catch (PackageManagerException pme) {
@@ -20096,7 +20145,7 @@
                 if (Process.isIsolated(uid)) {
                     return Zygote.MOUNT_EXTERNAL_NONE;
                 }
-                if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+                if (StorageManager.hasIsolatedStorage()) {
                     return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
                             ? Zygote.MOUNT_EXTERNAL_FULL
                             : Zygote.MOUNT_EXTERNAL_WRITE;
@@ -21698,7 +21747,6 @@
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final PackageManager pm = mContext.getPackageManager();
 
-        final boolean currentAsec;
         final String currentVolumeUuid;
         final File codeFile;
         final String installerPackageName;
@@ -21732,22 +21780,13 @@
                         "3rd party apps are not allowed on internal storage");
             }
 
-            if (pkg.applicationInfo.isExternalAsec()) {
-                currentAsec = true;
-                currentVolumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
-            } else if (pkg.applicationInfo.isForwardLocked()) {
-                currentAsec = true;
-                currentVolumeUuid = "forward_locked";
-            } else {
-                currentAsec = false;
-                currentVolumeUuid = ps.volumeUuid;
+            currentVolumeUuid = ps.volumeUuid;
 
-                final File probe = new File(pkg.codePath);
-                final File probeOat = new File(probe, "oat");
-                if (!probe.isDirectory() || !probeOat.isDirectory()) {
-                    throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
-                            "Move only supported for modern cluster style installs");
-                }
+            final File probe = new File(pkg.codePath);
+            final File probeOat = new File(probe, "oat");
+            if (!probe.isDirectory() || !probeOat.isDirectory()) {
+                throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
+                        "Move only supported for modern cluster style installs");
             }
 
             if (Objects.equals(currentVolumeUuid, volumeUuid)) {
@@ -21784,12 +21823,11 @@
         final boolean moveCompleteApp;
         final File measurePath;
 
+        installFlags = INSTALL_INTERNAL;
         if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
-            installFlags = INSTALL_INTERNAL;
-            moveCompleteApp = !currentAsec;
+            moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
-            installFlags = INSTALL_EXTERNAL;
             moveCompleteApp = false;
             measurePath = storage.getPrimaryPhysicalVolume().getPath();
         } else {
@@ -21801,9 +21839,6 @@
                         "Move location not mounted private volume");
             }
 
-            Preconditions.checkState(!currentAsec);
-
-            installFlags = INSTALL_INTERNAL;
             moveCompleteApp = true;
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         }
@@ -22588,7 +22623,7 @@
         private SigningDetails getSigningDetails(int uid) {
             synchronized (mPackages) {
                 final int appId = UserHandle.getAppId(uid);
-                final Object obj = mSettings.getUserIdLPr(appId);
+                final Object obj = mSettings.getSettingLPr(appId);
                 if (obj != null) {
                     if (obj instanceof SharedUserSetting) {
                         return ((SharedUserSetting) obj).signatures.mSigningDetails;
@@ -23181,6 +23216,45 @@
                 throws IOException {
             PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags);
         }
+
+        @Override
+        public void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+            PackageManagerService.this.forEachPackage(actionLocked);
+        }
+
+        @Override
+        public ArraySet<String> getEnabledComponents(String packageName, int userId) {
+            synchronized (mPackages) {
+                PackageSetting setting = mSettings.getPackageLPr(packageName);
+                if (setting == null) {
+                    return new ArraySet<>();
+                }
+                return setting.getEnabledComponents(userId);
+            }
+        }
+
+        @Override
+        public ArraySet<String> getDisabledComponents(String packageName, int userId) {
+            synchronized (mPackages) {
+                PackageSetting setting = mSettings.getPackageLPr(packageName);
+                if (setting == null) {
+                    return new ArraySet<>();
+                }
+                return setting.getDisabledComponents(userId);
+            }
+        }
+
+        @Override
+        public @PackageManager.EnabledState int getApplicationEnabledState(
+                String packageName, int userId) {
+            synchronized (mPackages) {
+                PackageSetting setting = mSettings.getPackageLPr(packageName);
+                if (setting == null) {
+                    return COMPONENT_ENABLED_STATE_DEFAULT;
+                }
+                return setting.getEnabled(userId);
+            }
+        }
     }
 
     @GuardedBy("mPackages")
@@ -23303,6 +23377,15 @@
         }
     }
 
+    void forEachPackage(Consumer<PackageParser.Package> actionLocked) {
+        synchronized (mPackages) {
+            int numPackages = mPackages.size();
+            for (int i = 0; i < numPackages; i++) {
+                actionLocked.accept(mPackages.valueAt(i));
+            }
+        }
+    }
+
     private static void enforceSystemOrPhoneCaller(String tag) {
         int callingUid = Binder.getCallingUid();
         if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
@@ -23590,6 +23673,30 @@
         return mProtectedPackages.isPackageStateProtected(userId, packageName);
     }
 
+    @Override
+    public void sendDeviceCustomizationReadyBroadcast() {
+        mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
+                "sendDeviceCustomizationReadyBroadcast");
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
+            intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            final IActivityManager am = ActivityManager.getService();
+            final String[] requiredPermissions = {
+                Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
+            };
+            try {
+                am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
+                        android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     static class ActiveInstallSession {
         private final String mPackageName;
         private final File mStagedDir;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6f275ec..77f8c3a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2193,9 +2193,6 @@
         boolean replaceExisting = true;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-l":
-                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
-                    break;
                 case "-r": // ignore
                     break;
                 case "-R":
@@ -2210,9 +2207,6 @@
                 case "-t":
                     sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
                     break;
-                case "-s":
-                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
-                    break;
                 case "-f":
                     sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
                     break;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index b850613..2c2cc7e 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -152,10 +152,6 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
     }
 
-    public boolean isForwardLocked() {
-        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
-    }
-
     public boolean isSystem() {
         return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 239dc99..fbf5439 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -64,7 +64,6 @@
                 | ApplicationInfo.PRIVATE_FLAG_VENDOR
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT
                 | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES
-                | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6009bd3..c524dba 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -381,11 +381,9 @@
     final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
             new SparseArray<CrossProfileIntentResolver>();
 
-    final ArrayMap<String, SharedUserSetting> mSharedUsers =
-            new ArrayMap<String, SharedUserSetting>();
-    private final ArrayList<Object> mUserIds = new ArrayList<Object>();
-    private final SparseArray<Object> mOtherUserIds =
-            new SparseArray<Object>();
+    final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
+    private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
+    private final SparseArray<SettingBase> mOtherAppIds = new SparseArray<>();
 
     // For reading/writing settings file.
     private final ArrayList<Signature> mPastSignatures =
@@ -519,7 +517,7 @@
         SharedUserSetting s = mSharedUsers.get(name);
         if (s == null && create) {
             s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
-            s.userId = newUserIdLPw(s);
+            s.userId = acquireAndRegisterNewAppIdLPw(s);
             if (s.userId < 0) {
                 // < 0 means we couldn't assign a userid; throw exception
                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
@@ -612,7 +610,7 @@
                 cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags, parentPackageName,
                 childPackageNames, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames);
         p.appId = uid;
-        if (addUserIdLPw(uid, p, name)) {
+        if (registerExistingAppIdLPw(uid, p, name)) {
             mPackages.put(name, p);
             return p;
         }
@@ -635,7 +633,7 @@
         }
         s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
         s.userId = uid;
-        if (addUserIdLPw(uid, s, name)) {
+        if (registerExistingAppIdLPw(uid, s, name)) {
             mSharedUsers.put(name, s);
             return s;
         }
@@ -885,13 +883,13 @@
      * Registers a user ID with the system. Potentially allocates a new user ID.
      * @throws PackageManagerException If a user ID could not be allocated.
      */
-    void addUserToSettingLPw(PackageSetting p) throws PackageManagerException {
+    void registerAppIdLPw(PackageSetting p) throws PackageManagerException {
         if (p.appId == 0) {
             // Assign new user ID
-            p.appId = newUserIdLPw(p);
+            p.appId = acquireAndRegisterNewAppIdLPw(p);
         } else {
             // Add new setting to list of user IDs
-            addUserIdLPw(p.appId, p, p.name);
+            registerExistingAppIdLPw(p.appId, p, p.name);
         }
         if (p.appId < 0) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -972,14 +970,14 @@
 
         // If the we know about this user id, we have to update it as it
         // has to point to the same PackageSetting instance as the package.
-        Object userIdPs = getUserIdLPr(p.appId);
+        Object userIdPs = getSettingLPr(p.appId);
         if (sharedUser == null) {
             if (userIdPs != null && userIdPs != p) {
-                replaceUserIdLPw(p.appId, p);
+                replaceAppIdLPw(p.appId, p);
             }
         } else {
             if (userIdPs != null && userIdPs != sharedUser) {
-                replaceUserIdLPw(p.appId, sharedUser);
+                replaceAppIdLPw(p.appId, sharedUser);
             }
         }
 
@@ -1083,11 +1081,11 @@
                 p.sharedUser.removePackage(p);
                 if (p.sharedUser.packages.size() == 0) {
                     mSharedUsers.remove(p.sharedUser.name);
-                    removeUserIdLPw(p.sharedUser.userId);
+                    removeAppIdLPw(p.sharedUser.userId);
                     return p.sharedUser.userId;
                 }
             } else {
-                removeUserIdLPw(p.appId);
+                removeAppIdLPw(p.appId);
                 return p.appId;
             }
         }
@@ -1115,65 +1113,69 @@
         mInstallerPackages.remove(packageName);
     }
 
-    private boolean addUserIdLPw(int uid, Object obj, Object name) {
-        if (uid > Process.LAST_APPLICATION_UID) {
+    /** Returns true if the requested AppID was valid and not already registered. */
+    private boolean registerExistingAppIdLPw(int appId, SettingBase obj, Object name) {
+        if (appId > Process.LAST_APPLICATION_UID) {
             return false;
         }
 
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            while (index >= N) {
-                mUserIds.add(null);
-                N++;
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            // fill the array until our index becomes valid
+            while (index >= size) {
+                mAppIds.add(null);
+                size++;
             }
-            if (mUserIds.get(index) != null) {
+            if (mAppIds.get(index) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
-                        "Adding duplicate user id: " + uid
+                        "Adding duplicate app id: " + appId
                         + " name=" + name);
                 return false;
             }
-            mUserIds.set(index, obj);
+            mAppIds.set(index, obj);
         } else {
-            if (mOtherUserIds.get(uid) != null) {
+            if (mOtherAppIds.get(appId) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
-                        "Adding duplicate shared id: " + uid
+                        "Adding duplicate shared id: " + appId
                                 + " name=" + name);
                 return false;
             }
-            mOtherUserIds.put(uid, obj);
+            mOtherAppIds.put(appId, obj);
         }
         return true;
     }
 
-    public Object getUserIdLPr(int uid) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            return index < N ? mUserIds.get(index) : null;
+    /** Gets the setting associated with the provided App ID */
+    public SettingBase getSettingLPr(int appId) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            return index < size ? mAppIds.get(index) : null;
         } else {
-            return mOtherUserIds.get(uid);
+            return mOtherAppIds.get(appId);
         }
     }
 
-    private void removeUserIdLPw(int uid) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            if (index < N) mUserIds.set(index, null);
+    /** Unregisters the provided app ID. */
+    void removeAppIdLPw(int appId) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            if (index < size) mAppIds.set(index, null);
         } else {
-            mOtherUserIds.remove(uid);
+            mOtherAppIds.remove(appId);
         }
-        setFirstAvailableUid(uid+1);
+        setFirstAvailableUid(appId + 1);
     }
 
-    private void replaceUserIdLPw(int uid, Object obj) {
-        if (uid >= Process.FIRST_APPLICATION_UID) {
-            final int N = mUserIds.size();
-            final int index = uid - Process.FIRST_APPLICATION_UID;
-            if (index < N) mUserIds.set(index, obj);
+    private void replaceAppIdLPw(int appId, SettingBase obj) {
+        if (appId >= Process.FIRST_APPLICATION_UID) {
+            final int size = mAppIds.size();
+            final int index = appId - Process.FIRST_APPLICATION_UID;
+            if (index < size) mAppIds.set(index, obj);
         } else {
-            mOtherUserIds.put(uid, obj);
+            mOtherAppIds.put(appId, obj);
         }
     }
 
@@ -3157,7 +3159,7 @@
         for (int i = 0; i < N; i++) {
             final PackageSetting p = mPendingPackages.get(i);
             final int sharedUserId = p.getSharedUserId();
-            final Object idObj = getUserIdLPr(sharedUserId);
+            final Object idObj = getSettingLPr(sharedUserId);
             if (idObj instanceof SharedUserSetting) {
                 final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
                 p.sharedUser = sharedUser;
@@ -3202,7 +3204,7 @@
         final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
         while (disabledIt.hasNext()) {
             final PackageSetting disabledPs = disabledIt.next();
-            final Object id = getUserIdLPr(disabledPs.appId);
+            final Object id = getSettingLPr(disabledPs.appId);
             if (id != null && id instanceof SharedUserSetting) {
                 disabledPs.sharedUser = (SharedUserSetting) id;
             }
@@ -3656,7 +3658,6 @@
 
     private static int PRE_M_APP_INFO_FLAG_HIDDEN = 1<<27;
     private static int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1<<28;
-    private static int PRE_M_APP_INFO_FLAG_FORWARD_LOCK = 1<<29;
     private static int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1<<30;
 
     private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -3756,15 +3757,11 @@
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
                     }
-                    if ((pkgFlags & PRE_M_APP_INFO_FLAG_FORWARD_LOCK) != 0) {
-                        pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK;
-                    }
                     if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) {
                         pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
                     }
                     pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN
                             | PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE
-                            | PRE_M_APP_INFO_FLAG_FORWARD_LOCK
                             | PRE_M_APP_INFO_FLAG_PRIVILEGED);
                 } else {
                     // For backward compatibility
@@ -4206,24 +4203,24 @@
         }
     }
 
-    // Returns -1 if we could not find an available UserId to assign
-    private int newUserIdLPw(Object obj) {
+    /** Returns a new AppID or -1 if we could not find an available AppID to assign */
+    private int acquireAndRegisterNewAppIdLPw(SettingBase obj) {
         // Let's be stupidly inefficient for now...
-        final int N = mUserIds.size();
-        for (int i = mFirstAvailableUid; i < N; i++) {
-            if (mUserIds.get(i) == null) {
-                mUserIds.set(i, obj);
+        final int size = mAppIds.size();
+        for (int i = mFirstAvailableUid; i < size; i++) {
+            if (mAppIds.get(i) == null) {
+                mAppIds.set(i, obj);
                 return Process.FIRST_APPLICATION_UID + i;
             }
         }
 
         // None left?
-        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
+        if (size > (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
             return -1;
         }
 
-        mUserIds.add(obj);
-        return Process.FIRST_APPLICATION_UID + N;
+        mAppIds.add(obj);
+        return Process.FIRST_APPLICATION_UID + size;
     }
 
     public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() {
@@ -4258,11 +4255,48 @@
         return false;
     }
 
+    /**
+     * Returns the disabled {@link PackageSetting} for the provided package name if one exists,
+     * {@code null} otherwise.
+     */
+    @Nullable
     public PackageSetting getDisabledSystemPkgLPr(String name) {
         PackageSetting ps = mDisabledSysPackages.get(name);
         return ps;
     }
 
+    /**
+     * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting}
+     * if one exists, {@code null} otherwise.
+     */
+    @Nullable
+    public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) {
+        if (enabledPackageSetting == null) {
+            return null;
+        }
+        return getDisabledSystemPkgLPr(enabledPackageSetting.name);
+    }
+
+    /**
+     * Fetches an array of the child {@link PackageSetting}s for all child package names referenced
+     * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced.
+     *
+     * Note: Any child packages not found will be null in the returned array.
+     */
+    @Nullable
+    public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) {
+        if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) {
+            return null;
+        }
+        final int childCount = parentPackageSetting.childPackageNames.size();
+        PackageSetting[] children =
+                new PackageSetting[childCount];
+        for (int i = 0; i < childCount; i++) {
+            children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i));
+        }
+        return children;
+    }
+
     boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
         final PackageSetting ps = mPackages.get(componentInfo.packageName);
         if (ps == null) return false;
@@ -4428,7 +4462,6 @@
             ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
             ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
             ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
-            ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
             ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
             ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
             ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
new file mode 100644
index 0000000..9e8b73e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2018 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.pm;
+
+import android.text.TextUtils;
+
+/**
+ * Represents a Share Target definition, read from the application's manifest (shortcuts.xml)
+ */
+class ShareTargetInfo {
+    static class TargetData {
+        final String mScheme;
+        final String mHost;
+        final String mPort;
+        final String mPath;
+        final String mPathPattern;
+        final String mPathPrefix;
+        final String mMimeType;
+
+        TargetData(String scheme, String host, String port, String path, String pathPattern,
+                String pathPrefix, String mimeType) {
+            mScheme = scheme;
+            mHost = host;
+            mPort = port;
+            mPath = path;
+            mPathPattern = pathPattern;
+            mPathPrefix = pathPrefix;
+            mMimeType = mimeType;
+        }
+
+        public void toStringInner(StringBuilder strBuilder) {
+            if (!TextUtils.isEmpty(mScheme)) {
+                strBuilder.append(" scheme=").append(mScheme);
+            }
+            if (!TextUtils.isEmpty(mHost)) {
+                strBuilder.append(" host=").append(mHost);
+            }
+            if (!TextUtils.isEmpty(mPort)) {
+                strBuilder.append(" port=").append(mPort);
+            }
+            if (!TextUtils.isEmpty(mPath)) {
+                strBuilder.append(" path=").append(mPath);
+            }
+            if (!TextUtils.isEmpty(mPathPattern)) {
+                strBuilder.append(" pathPattern=").append(mPathPattern);
+            }
+            if (!TextUtils.isEmpty(mPathPrefix)) {
+                strBuilder.append(" pathPrefix=").append(mPathPrefix);
+            }
+            if (!TextUtils.isEmpty(mMimeType)) {
+                strBuilder.append(" mimeType=").append(mMimeType);
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder strBuilder = new StringBuilder();
+            toStringInner(strBuilder);
+            return strBuilder.toString();
+        }
+    }
+
+    final TargetData[] mTargetData;
+    final String mTargetClass;
+    final String[] mCategories;
+
+    ShareTargetInfo(TargetData[] data, String targetClass, String[] categories) {
+        mTargetData = data;
+        mTargetClass = targetClass;
+        mCategories = categories;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder strBuilder = new StringBuilder();
+        strBuilder.append("targetClass=").append(mTargetClass);
+        for (int i = 0; i < mTargetData.length; i++) {
+            strBuilder.append(" data={");
+            mTargetData[i].toStringInner(strBuilder);
+            strBuilder.append("}");
+        }
+        for (int i = 0; i < mCategories.length; i++) {
+            strBuilder.append(" category=").append(mCategories[i]);
+        }
+
+        return strBuilder.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 92e261a..83f0fde 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -111,6 +111,11 @@
     final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
 
     /**
+     * All the share targets from the package
+     */
+    private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
+
+    /**
      * # of times the package has called rate-limited APIs.
      */
     private int mApiCallCount;
@@ -739,15 +744,16 @@
         List<ShortcutInfo> newManifestShortcutList = null;
         try {
             newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
-                    getPackageName(), getPackageUserId());
+                    getPackageName(), getPackageUserId(), mShareTargets);
         } catch (IOException|XmlPullParserException e) {
             Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
         }
         final int manifestShortcutSize = newManifestShortcutList == null ? 0
                 : newManifestShortcutList.size();
         if (ShortcutService.DEBUG) {
-            Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
-                    getPackageName(), manifestShortcutSize));
+            Slog.d(TAG,
+                    String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
+                            getPackageName(), manifestShortcutSize, mShareTargets.size()));
         }
         if (isNewApp && (manifestShortcutSize == 0)) {
             // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
@@ -1657,6 +1663,11 @@
         return new ArrayList<>(mShortcuts.values());
     }
 
+    @VisibleForTesting
+    List<ShareTargetInfo> getAllShareTargetsForTest() {
+        return new ArrayList<>(mShareTargets);
+    }
+
     @Override
     public void verifyStates() {
         super.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 866c46c..90f08c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
@@ -55,10 +56,14 @@
     private static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_INTENT = "intent";
     private static final String TAG_CATEGORIES = "categories";
+    private static final String TAG_SHARE_TARGET = "share-target";
+    private static final String TAG_DATA = "data";
+    private static final String TAG_CATEGORY = "category";
 
     @Nullable
-    public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
-            String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+    public static List<ShortcutInfo> parseShortcuts(ShortcutService service, String packageName,
+            @UserIdInt int userId, @NonNull List<ShareTargetInfo> outShareTargets)
+            throws IOException, XmlPullParserException {
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format("Scanning package %s for manifest shortcuts on user %d",
                     packageName, userId));
@@ -69,6 +74,7 @@
         }
 
         List<ShortcutInfo> result = null;
+        outShareTargets.clear();
 
         try {
             final int size = activities.size();
@@ -82,8 +88,8 @@
                         service.getActivityInfoWithMetadata(
                         activityInfoNoMetadata.getComponentName(), userId);
                 if (activityInfoWithMetadata != null) {
-                    result = parseShortcutsOneFile(
-                            service, activityInfoWithMetadata, packageName, userId, result);
+                    result = parseShortcutsOneFile(service, activityInfoWithMetadata, packageName,
+                            userId, result, outShareTargets);
                 }
             }
         } catch (RuntimeException e) {
@@ -99,7 +105,8 @@
     private static List<ShortcutInfo> parseShortcutsOneFile(
             ShortcutService service,
             ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
-            List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+            List<ShortcutInfo> result, @NonNull List<ShareTargetInfo> outShareTargets)
+            throws IOException, XmlPullParserException {
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format(
                     "Checking main activity %s", activityInfo.getComponentName()));
@@ -126,9 +133,19 @@
             // after parsing <intent>.  We keep the current one in here.
             ShortcutInfo currentShortcut = null;
 
+            // We instantiate ShareTargetInfo at <share-target>, but add it to outShareTargets at
+            // </share-target>, after parsing <data> and <category>. We keep the current one here.
+            ShareTargetInfo currentShareTarget = null;
+
+            // Keeps parsed categories for both ShortcutInfo and ShareTargetInfo
             Set<String> categories = null;
+
+            // Keeps parsed intents for ShortcutInfo
             final ArrayList<Intent> intents = new ArrayList<>();
 
+            // Keeps parsed data fields for ShareTargetInfo
+            final ArrayList<ShareTargetInfo.TargetData> dataList = new ArrayList<>();
+
             outer:
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
@@ -194,6 +211,32 @@
                     continue;
                 }
 
+                // When a share-target tag is closing, publish.
+                if ((type == XmlPullParser.END_TAG) && (depth == 2)
+                        && (TAG_SHARE_TARGET.equals(tag))) {
+                    if (currentShareTarget == null) {
+                        // ShareTarget was invalid.
+                        continue;
+                    }
+                    final ShareTargetInfo sti = currentShareTarget;
+                    currentShareTarget = null; // Make sure to null out for the next iteration.
+
+                    if (categories == null || categories.isEmpty() || dataList.isEmpty()) {
+                        // Incomplete ShareTargetInfo.
+                        continue;
+                    }
+
+                    final ShareTargetInfo newShareTarget = new ShareTargetInfo(
+                            dataList.toArray(new ShareTargetInfo.TargetData[dataList.size()]),
+                            sti.mTargetClass, categories.toArray(new String[categories.size()]));
+                    outShareTargets.add(newShareTarget);
+                    if (ShortcutService.DEBUG) {
+                        Slog.d(TAG, "ShareTarget added: " + newShareTarget.toString());
+                    }
+                    categories = null;
+                    dataList.clear();
+                }
+
                 // Otherwise, just look at start tags.
                 if (type != XmlPullParser.START_TAG) {
                     continue;
@@ -224,6 +267,17 @@
                     categories = null;
                     continue;
                 }
+                if (depth == 2 && TAG_SHARE_TARGET.equals(tag)) {
+                    final ShareTargetInfo sti = parseShareTargetAttributes(service, attrs);
+                    if (sti == null) {
+                        // ShareTarget was invalid.
+                        continue;
+                    }
+                    currentShareTarget = sti;
+                    categories = null;
+                    dataList.clear();
+                    continue;
+                }
                 if (depth == 3 && TAG_INTENT.equals(tag)) {
                     if ((currentShortcut == null)
                             || !currentShortcut.isEnabled()) {
@@ -258,6 +312,34 @@
                     categories.add(name);
                     continue;
                 }
+                if (depth == 3 && TAG_CATEGORY.equals(tag)) {
+                    if ((currentShareTarget == null)) {
+                        continue;
+                    }
+                    final String name = parseCategory(service, attrs);
+                    if (TextUtils.isEmpty(name)) {
+                        Log.e(TAG, "Empty category found. activity=" + activity);
+                        continue;
+                    }
+
+                    if (categories == null) {
+                        categories = new ArraySet<>();
+                    }
+                    categories.add(name);
+                    continue;
+                }
+                if (depth == 3 && TAG_DATA.equals(tag)) {
+                    if ((currentShareTarget == null)) {
+                        continue;
+                    }
+                    final ShareTargetInfo.TargetData data = parseShareTargetData(service, attrs);
+                    if (data == null) {
+                        Log.e(TAG, "Invalid data tag found. activity=" + activity);
+                        continue;
+                    }
+                    dataList.add(data);
+                    continue;
+                }
 
                 Log.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
             }
@@ -369,4 +451,57 @@
                 null, // bitmap path
                 disabledReason);
     }
+
+    private static String parseCategory(ShortcutService service, AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.IntentCategory);
+        try {
+            if (sa.getType(R.styleable.IntentCategory_name) != TypedValue.TYPE_STRING) {
+                Log.w(TAG, "android:name must be string literal.");
+                return null;
+            }
+            return sa.getString(R.styleable.IntentCategory_name);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ShareTargetInfo parseShareTargetAttributes(ShortcutService service,
+            AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.Intent);
+        try {
+            String targetClass = sa.getString(R.styleable.Intent_targetClass);
+            if (TextUtils.isEmpty(targetClass)) {
+                Log.w(TAG, "android:targetClass must be provided.");
+                return null;
+            }
+            return new ShareTargetInfo(null, targetClass, null);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private static ShareTargetInfo.TargetData parseShareTargetData(ShortcutService service,
+            AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.AndroidManifestData);
+        try {
+            if (sa.getType(R.styleable.AndroidManifestData_mimeType) != TypedValue.TYPE_STRING) {
+                Log.w(TAG, "android:mimeType must be string literal.");
+                return null;
+            }
+            String scheme = sa.getString(R.styleable.AndroidManifestData_scheme);
+            String host = sa.getString(R.styleable.AndroidManifestData_host);
+            String port = sa.getString(R.styleable.AndroidManifestData_port);
+            String path = sa.getString(R.styleable.AndroidManifestData_path);
+            String pathPattern = sa.getString(R.styleable.AndroidManifestData_pathPattern);
+            String pathPrefix = sa.getString(R.styleable.AndroidManifestData_pathPrefix);
+            String mimeType = sa.getString(R.styleable.AndroidManifestData_mimeType);
+            return new ShareTargetInfo.TargetData(scheme, host, port, path, pathPattern, pathPrefix,
+                    mimeType);
+        } finally {
+            sa.recycle();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index b9c3048..2b773f4 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -41,8 +41,8 @@
 import android.content.pm.LauncherApps.ShortcutQuery;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
@@ -98,8 +98,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
 import com.android.internal.util.StatLogger;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
 
@@ -1172,7 +1172,7 @@
                 return true;
             }
         }
-        
+
         // If the local copy says the user is locked, check with AM for the actual state, since
         // the user might just have been unlocked.
         // Note we just don't use isUserUnlockingOrUnlocked() here, because it'll return false
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 505e4ee..1fd9b69 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -294,13 +294,14 @@
      */
     public void detectLocaleChange() {
         final String currentLocales = mService.injectGetLocaleTagsForUser(mUserId);
-        if (getKnownLocales().equals(currentLocales)) {
+        if (!TextUtils.isEmpty(mKnownLocales) && mKnownLocales.equals(currentLocales)) {
             return;
         }
         if (ShortcutService.DEBUG) {
-            Slog.d(TAG, "Locale changed from " + currentLocales + " to " + mKnownLocales
+            Slog.d(TAG, "Locale changed from " + mKnownLocales + " to " + currentLocales
                     + " for user " + mUserId);
         }
+
         mKnownLocales = currentLocales;
 
         forAllPackages(pkg -> {
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index e194d15..2d583ca3 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -105,6 +105,8 @@
      */
     private boolean perUser;
 
+    boolean usageInfoRequired;
+
     public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) {
         name = _name;
         sourcePackageName = _sourcePackageName;
@@ -351,6 +353,7 @@
         }
         if (bp.perm == p) {
             bp.protectionLevel = p.info.protectionLevel;
+            bp.usageInfoRequired = p.info.usageInfoRequired;
         }
         if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) {
             Log.d(TAG, "  Permissions: " + r);
@@ -430,6 +433,7 @@
         permissionInfo.packageName = sourcePackageName;
         permissionInfo.nonLocalizedLabel = name;
         permissionInfo.protectionLevel = protectionLevel;
+        permissionInfo.usageInfoRequired = usageInfoRequired;
         return permissionInfo;
     }
 
@@ -458,6 +462,7 @@
         bp.protectionLevel = readInt(parser, null, "protection",
                 PermissionInfo.PROTECTION_NORMAL);
         bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+        bp.usageInfoRequired = readInt(parser, null, "usageInfoRequired", 0) != 0;
         if (dynamic) {
             final PermissionInfo pi = new PermissionInfo();
             pi.packageName = sourcePackage.intern();
@@ -465,6 +470,7 @@
             pi.icon = readInt(parser, null, "icon", 0);
             pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
             pi.protectionLevel = bp.protectionLevel;
+            pi.usageInfoRequired = bp.usageInfoRequired;
             bp.pendingPermissionInfo = pi;
         }
         out.put(bp.name, bp);
@@ -497,6 +503,7 @@
         if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) {
             serializer.attribute(null, "protection", Integer.toString(protectionLevel));
         }
+        serializer.attribute(null, "usageInfoRequired", usageInfoRequired ? "1" : "0");
         if (type == BasePermission.TYPE_DYNAMIC) {
             final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo;
             if (pi != null) {
@@ -533,6 +540,7 @@
         if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
         // We'll take care of setting this one.
         if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+        if (pi1.usageInfoRequired != pi2.usageInfoRequired) return false;
         // These are not currently stored in settings.
         //if (!compareStrings(pi1.group, pi2.group)) return false;
         //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -580,6 +588,8 @@
             pw.print("    enforced=");
             pw.println(readEnforced);
         }
+        pw.print("    usageInfoRequired=");
+        pw.println(usageInfoRequired);
         return true;
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 21cc14e..51619cf 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -46,7 +46,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager;
@@ -144,6 +143,11 @@
         LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
     }
 
+    private static final Set<String> ACTIVITY_RECOGNITION_PERMISSIONS = new ArraySet<>();
+    static {
+        ACTIVITY_RECOGNITION_PERMISSIONS.add(Manifest.permission.ACTIVITY_RECOGNITION);
+    }
+
     private static final Set<String> COARSE_LOCATION_PERMISSIONS = new ArraySet<>();
     static {
         COARSE_LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION);
@@ -184,7 +188,7 @@
     private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
     static {
         // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (!StorageManager.hasIsolatedStorage()) {
             STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
             STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
         }
@@ -193,7 +197,7 @@
     private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
     static {
         // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (StorageManager.hasIsolatedStorage()) {
             MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
         }
     }
@@ -201,7 +205,7 @@
     private static final Set<String> MEDIA_VISUAL_PERMISSIONS = new ArraySet<>();
     static {
         // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+        if (StorageManager.hasIsolatedStorage()) {
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
             MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
         }
@@ -624,7 +628,7 @@
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
                         SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
                 grantSystemFixedPermissionsToSystemPackage(packageName, userId,
-                        LOCATION_PERMISSIONS);
+                        LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
 
@@ -1054,6 +1058,17 @@
             return;
         }
 
+        // Intersect the requestedPermissions for a factory image with that of its current update
+        // in case the latter one removed a <uses-permission>
+        String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;
+        int size = requestedPermissions.length;
+        for (int i = 0; i < size; i++) {
+            if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {
+                requestedPermissions[i] = null;
+            }
+        }
+        requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);
+
         PackageManager pm = mContext.getPackageManager();
         final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
         ApplicationInfo applicationInfo = pkg.applicationInfo;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index ec15c16..189d0f4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -181,4 +181,9 @@
 
     /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */
     public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName);
-}
\ No newline at end of file
+
+    /**
+     * Returns {@code true} if {@code permName} has {@code usageInfoRequired} set.
+     */
+    public abstract boolean isPermissionUsageInfoRequired(@NonNull String permName);
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index c5d38db..4406fdd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2644,5 +2644,12 @@
                 return mSettings.getPermissionLocked(permName);
             }
         }
+        @Override
+        public boolean isPermissionUsageInfoRequired(String permName) {
+            synchronized (PermissionManagerService.this.mLock) {
+                BasePermission bp = mSettings.getPermissionLocked(permName);
+                return bp != null && bp.usageInfoRequired;
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index 4124210..b5ad235 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -30,21 +30,32 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.Signature;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.PackageUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
+import java.io.ByteArrayOutputStream;
+import java.io.FileDescriptor;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -96,7 +107,7 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         getContext().registerReceiverAsUser(new BroadcastReceiver() {
             @Override
-            public void onReceive(Context context, Intent intent) {
+            public void onReceive(@NonNull Context context, @NonNull Intent intent) {
                 if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
                     int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                     onRemoveUser(userId);
@@ -114,13 +125,19 @@
 
     @Override
     public void onStartUser(@UserIdInt int userId) {
+        RoleUserState userState;
         synchronized (mLock) {
-            //TODO only call into PermissionController if it or system upgreaded (for boot time)
-            getUserStateLocked(userId);
+            userState = getUserStateLocked(userId);
         }
-        //TODO consider calling grants only when certain conditions are met
-        // such as OS or PermissionController upgrade
-        if (RemoteRoleControllerService.DEBUG) {
+        String packagesHash = computeComponentStateHash(userId);
+        String lastGrantPackagesHash;
+        synchronized (mLock) {
+            lastGrantPackagesHash = userState.getLastGrantPackagesHashLocked();
+        }
+        boolean needGrant = !Objects.equals(packagesHash, lastGrantPackagesHash);
+        if (needGrant) {
+            // Some vital packages state has changed since last role grant
+            // Run grants again
             Slog.i(LOG_TAG, "Granting default permissions...");
             CompletableFuture<Void> result = new CompletableFuture<>();
             getControllerService(userId).onGrantDefaultRoles(
@@ -129,7 +146,6 @@
                         public void onSuccess() {
                             result.complete(null);
                         }
-
                         @Override
                         public void onFailure() {
                             result.completeExceptionally(new RuntimeException());
@@ -137,19 +153,54 @@
                     });
             try {
                 result.get(5, TimeUnit.SECONDS);
+                synchronized (mLock) {
+                    userState.setLastGrantPackagesHashLocked(packagesHash);
+                }
             } catch (InterruptedException | ExecutionException | TimeoutException e) {
                 Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
             }
+        } else if (RemoteRoleControllerService.DEBUG) {
+            Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash);
         }
     }
 
+    @Nullable
+    private String computeComponentStateHash(@UserIdInt int userId) {
+        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> {
+            out.write(pkg.packageName.getBytes());
+            out.write(BitUtils.toBytes(pkg.getLongVersionCode()));
+            out.write(pm.getApplicationEnabledState(pkg.packageName, userId));
+
+            ArraySet<String> enabledComponents =
+                    pm.getEnabledComponents(pkg.packageName, userId);
+            int numComponents = CollectionUtils.size(enabledComponents);
+            for (int i = 0; i < numComponents; i++) {
+                out.write(enabledComponents.valueAt(i).getBytes());
+            }
+
+            ArraySet<String> disabledComponents =
+                    pm.getDisabledComponents(pkg.packageName, userId);
+            numComponents = CollectionUtils.size(disabledComponents);
+            for (int i = 0; i < numComponents; i++) {
+                out.write(disabledComponents.valueAt(i).getBytes());
+            }
+            for (Signature signature : pkg.mSigningDetails.signatures) {
+                out.write(signature.toByteArray());
+            }
+        }));
+
+        return PackageUtils.computeSha256Digest(out.toByteArray());
+    }
+
     @GuardedBy("mLock")
     @NonNull
     private RoleUserState getUserStateLocked(@UserIdInt int userId) {
         RoleUserState userState = mUserStates.get(userId);
         if (userState == null) {
-            userState = new RoleUserState(userId);
-            userState.readSyncLocked();
+            userState = RoleUserState.newInstanceLocked(userId);
             mUserStates.put(userId, userState);
         }
         return userState;
@@ -334,5 +385,13 @@
             return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                     false, true, name, null);
         }
+
+        @Override
+        public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+                @Nullable FileDescriptor err, @NonNull String[] args,
+                @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+            new RoleManagerShellCommand(this).exec(this, in, out, err, args, callback,
+                    resultReceiver);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/role/RoleManagerShellCommand.java b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
new file mode 100644
index 0000000..336b311
--- /dev/null
+++ b/services/core/java/com/android/server/role/RoleManagerShellCommand.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 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.role;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.role.IRoleManager;
+import android.app.role.IRoleManagerCallback;
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+class RoleManagerShellCommand extends ShellCommand {
+
+    @NonNull
+    private final IRoleManager mRoleManager;
+
+    RoleManagerShellCommand(@NonNull IRoleManager roleManager) {
+        mRoleManager = roleManager;
+    }
+
+    private class Callback extends IRoleManagerCallback.Stub {
+
+        @NonNull
+        private final CompletableFuture<Void> mResult = new CompletableFuture<>();
+
+        public int waitForResult() {
+            try {
+                mResult.get(5, TimeUnit.SECONDS);
+                return 0;
+            } catch (Exception e) {
+                getErrPrintWriter().println("Error: " + e.toString());
+                return -1;
+            }
+        }
+
+        @Override
+        public void onSuccess() {
+            mResult.complete(null);
+        }
+
+        @Override
+        public void onFailure() {
+            mResult.completeExceptionally(new RuntimeException("Failed"));
+        }
+    }
+
+    @Override
+    public int onCommand(@Nullable String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+
+        PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "add-role-holder":
+                    return runAddRoleHolder();
+                case "remove-role-holder":
+                    return runRemoveRoleHolder();
+                case "clear-role-holders":
+                    return runClearRoleHolders();
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int getUserIdMaybe() {
+        int userId = UserHandle.USER_SYSTEM;
+        String option = getNextOption();
+        if (option != null && option.equals("--user")) {
+            userId = UserHandle.parseUserArg(getNextArgRequired());
+        }
+        return userId;
+    }
+
+    private int runAddRoleHolder() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+        String packageName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.addRoleHolderAsUser(roleName, packageName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    private int runRemoveRoleHolder() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+        String packageName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.removeRoleHolderAsUser(roleName, packageName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    private int runClearRoleHolders() throws RemoteException {
+        int userId = getUserIdMaybe();
+        String roleName = getNextArgRequired();
+
+        Callback callback = new Callback();
+        mRoleManager.clearRoleHoldersAsUser(roleName, userId, callback);
+        return callback.waitForResult();
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Role manager (role) commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("  add-role-holder [--user USER_ID] ROLE PACKAGE");
+        pw.println("  remove-role-holder [--user USER_ID] ROLE PACKAGE");
+        pw.println("  clear-role-holders [--user USER_ID] ROLE");
+        pw.println();
+    }
+}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 9c43f4d..3e3e156 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
 
 import libcore.io.IoUtils;
@@ -46,6 +47,7 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Stores the state of roles for a user.
@@ -63,26 +65,47 @@
     private static final String TAG_HOLDER = "holder";
     private static final String ATTRIBUTE_VERSION = "version";
     private static final String ATTRIBUTE_NAME = "name";
+    private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
 
     @UserIdInt
     private final int mUserId;
 
     @GuardedBy("RoleManagerService.mLock")
-    private int mVersion;
+    private int mVersion = VERSION_UNDEFINED;
+
+    @GuardedBy("RoleManagerService.mLock")
+    @Nullable
+    private String mLastGrantPackagesHash;
 
     /**
      * Maps role names to its holders' package names. The values should never be null.
      */
     @GuardedBy("RoleManagerService.mLock")
-    private ArrayMap<String, ArraySet<String>> mRoles;
+    @NonNull
+    private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
 
     @GuardedBy("RoleManagerService.mLock")
     private boolean mDestroyed;
 
+    @NonNull
     private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
 
-    public RoleUserState(@UserIdInt int userId) {
+    private RoleUserState(@UserIdInt int userId) {
         mUserId = userId;
+
+        readSyncLocked();
+    }
+
+    /**
+     * Create a new instance of user state, and read its state from disk if previously persisted.
+     *
+     * @param userId the user id for the new user state
+     *
+     * @return the new user state
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
+        return new RoleUserState(userId);
     }
 
     /**
@@ -110,6 +133,31 @@
     }
 
     /**
+     * Get the hash representing the state of packages during the last time initial grants was run.
+     *
+     * @return the hash representing the state of packages
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public String getLastGrantPackagesHashLocked() {
+        return mLastGrantPackagesHash;
+    }
+
+    /**
+     * Set the hash representing the state of packages during the last time initial grants was run.
+     *
+     * @param lastGrantPackagesHash the hash representing the state of packages
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void setLastGrantPackagesHashLocked(@Nullable String lastGrantPackagesHash) {
+        throwIfDestroyedLocked();
+        if (Objects.equals(mLastGrantPackagesHash, lastGrantPackagesHash)) {
+            return;
+        }
+        mLastGrantPackagesHash = lastGrantPackagesHash;
+        writeAsyncLocked();
+    }
+
+    /**
      * Get whether the role is available.
      *
      * @param roleName the name of the role to get the holders for
@@ -229,22 +277,24 @@
     @GuardedBy("RoleManagerService.mLock")
     private void writeAsyncLocked() {
         throwIfDestroyedLocked();
-        int version = mVersion;
+
         ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
-        for (int i = 0, size = mRoles.size(); i < size; ++i) {
+        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
             String roleName = mRoles.keyAt(i);
             ArraySet<String> roleHolders = mRoles.valueAt(i);
             roleHolders = new ArraySet<>(roleHolders);
             roles.put(roleName, roleHolders);
         }
+
         mWriteHandler.removeCallbacksAndMessages(null);
         // TODO: Throttle writes.
-        mWriteHandler.sendMessage(PooledLambda.obtainMessage(
-                RoleUserState::writeSync, this, version, roles));
+        mWriteHandler.sendMessage(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
+                mVersion, mLastGrantPackagesHash, roles));
     }
 
     @WorkerThread
-    private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) {
+    private void writeSync(int version, @Nullable String packagesHash,
+            @NonNull ArrayMap<String, ArraySet<String>> roles) {
         AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
         FileOutputStream out = null;
         try {
@@ -256,7 +306,7 @@
                     "http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
 
-            serializeRoles(serializer, version, roles);
+            serializeRoles(serializer, version, packagesHash, roles);
 
             serializer.endDocument();
             atomicFile.finishWrite(out);
@@ -272,17 +322,26 @@
 
     @WorkerThread
     private void serializeRoles(@NonNull XmlSerializer serializer, int version,
-            @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException {
+            @Nullable String packagesHash, @NonNull ArrayMap<String, ArraySet<String>> roles)
+            throws IOException {
         serializer.startTag(null, TAG_ROLES);
+
         serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
+
+        if (packagesHash != null) {
+            serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
+        }
+
         for (int i = 0, size = roles.size(); i < size; ++i) {
             String roleName = roles.keyAt(i);
             ArraySet<String> roleHolders = roles.valueAt(i);
+
             serializer.startTag(null, TAG_ROLE);
             serializer.attribute(null, ATTRIBUTE_NAME, roleName);
             serializeRoleHolders(serializer, roleHolders);
             serializer.endTag(null, TAG_ROLE);
         }
+
         serializer.endTag(null, TAG_ROLES);
     }
 
@@ -291,6 +350,7 @@
             @NonNull ArraySet<String> roleHolders) throws IOException {
         for (int i = 0, size = roleHolders.size(); i < size; ++i) {
             String roleHolder = roleHolders.valueAt(i);
+
             serializer.startTag(null, TAG_HOLDER);
             serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
             serializer.endTag(null, TAG_HOLDER);
@@ -301,11 +361,7 @@
      * Read the state from file.
      */
     @GuardedBy("RoleManagerService.mLock")
-    public void readSyncLocked() {
-        if (mRoles != null) {
-            throw new IllegalStateException("This RoleUserState has already read the roles.xml");
-        }
-
+    private void readSyncLocked() {
         File file = getFile(mUserId);
         try (FileInputStream in = new AtomicFile(file).openRead()) {
             XmlPullParser parser = Xml.newPullParser();
@@ -313,8 +369,6 @@
             parseXmlLocked(parser);
         } catch (FileNotFoundException e) {
             Slog.i(LOG_TAG, "roles.xml not found");
-            mRoles = new ArrayMap<>();
-            mVersion = VERSION_UNDEFINED;
         } catch (XmlPullParserException | IOException e) {
             throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
         }
@@ -336,12 +390,14 @@
                 return;
             }
         }
+        Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
     }
 
     private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
             XmlPullParserException {
         mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
-        mRoles = new ArrayMap<>();
+        mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
+        mRoles.clear();
 
         int type;
         int depth;
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
similarity index 69%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
index 27d25b8..7ce071f 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigApplicator.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.server.signedconfig;
+
+import android.content.Context;
+
+class SignedConfigApplicator {
+
+    static void applyConfig(Context context, String config, String signature) {
+        //TODO verify signature
+        //TODO parse & apply config
+    }
+
 }
diff --git a/services/core/java/com/android/server/signedconfig/SignedConfigService.java b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
new file mode 100644
index 0000000..1485686
--- /dev/null
+++ b/services/core/java/com/android/server/signedconfig/SignedConfigService.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 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.signedconfig;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+
+/**
+ * Signed config service. This is not an Android Service, but just owns a broadcast receiver for
+ * receiving package install and update notifications from the package manager.
+ */
+public class SignedConfigService {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "SignedConfig";
+
+    // TODO should these be elsewhere? In a public API?
+    private static final String KEY_CONFIG = "android.signedconfig";
+    private static final String KEY_CONFIG_SIGNATURE = "android.signedconfig.signature";
+
+    private static class UpdateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            new SignedConfigService(context).handlePackageBroadcast(intent);
+        }
+    }
+
+    private final Context mContext;
+    private final PackageManagerInternal mPacMan;
+
+    public SignedConfigService(Context context) {
+        mContext = context;
+        mPacMan = LocalServices.getService(PackageManagerInternal.class);
+    }
+
+    void handlePackageBroadcast(Intent intent) {
+        if (DBG) Slog.d(TAG, "handlePackageBroadcast " + intent);
+        Uri packageData = intent.getData();
+        String packageName = packageData == null ? null : packageData.getSchemeSpecificPart();
+        if (DBG) Slog.d(TAG, "handlePackageBroadcast package=" + packageName);
+        if (packageName == null) {
+            return;
+        }
+        int userId = mContext.getUser().getIdentifier();
+        PackageInfo pi = mPacMan.getPackageInfo(packageName, PackageManager.GET_META_DATA,
+                android.os.Process.SYSTEM_UID, userId);
+        if (pi == null) {
+            Slog.w(TAG, "Got null PackageInfo for " + packageName + "; user " + userId);
+            return;
+        }
+        Bundle metaData = pi.applicationInfo.metaData;
+        if (metaData == null) {
+            if (DBG) Slog.d(TAG, "handlePackageBroadcast: no metadata");
+            return;
+        }
+        if (metaData.containsKey(KEY_CONFIG)
+                && metaData.containsKey(KEY_CONFIG_SIGNATURE)) {
+            String config = metaData.getString(KEY_CONFIG);
+            String signature = metaData.getString(KEY_CONFIG_SIGNATURE);
+            if (DBG) {
+                Slog.d(TAG, "Got signed config: " + config);
+                Slog.d(TAG, "Got config signature: " + signature);
+            }
+            SignedConfigApplicator.applyConfig(mContext, config, signature);
+        } else {
+            if (DBG) Slog.d(TAG, "Package has no config/signature.");
+        }
+    }
+
+    /**
+     * Register to receive broadcasts from the package manager.
+     */
+    public static void registerUpdateReceiver(Context context) {
+        if (DBG) Slog.d(TAG, "Registering receiver");
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addDataScheme("package");
+        context.registerReceiver(new UpdateReceiver(), filter);
+    }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 3179ce9..f0ebb75 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -51,6 +51,7 @@
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -194,12 +195,11 @@
             "/system/bin/traced",  // Perfetto.
             "/system/bin/traced_probes",  // Perfetto.
             "webview_zygote",
-            // Temporarily excluded zygote to investigate its forking consequences in
-            // NativeProcessMemoryState.
-            // "zygote",
-            // "zygote64",
+            "zygote",
+            "zygote64",
     };
 
+    private static final int CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES = 8;
 
     static final class CompanionHandler extends Handler {
         CompanionHandler(Looper looper) {
@@ -1079,7 +1079,7 @@
             e.writeLong(processMemoryState.rssInBytes);
             e.writeLong(processMemoryState.cacheInBytes);
             e.writeLong(processMemoryState.swapInBytes);
-            e.writeLong(processMemoryState.rssHighWatermarkInBytes);
+            e.writeLong(0);  // unused
             e.writeLong(processMemoryState.startTimeNanos);
             pulledData.add(e);
         }
@@ -1088,6 +1088,7 @@
     private void pullNativeProcessMemoryState(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
+        final List<String> processNames = Arrays.asList(MEMORY_INTERESTING_NATIVE_PROCESSES);
         int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
         for (int i = 0; i < pids.length; i++) {
             int pid = pids[i];
@@ -1097,13 +1098,19 @@
             }
             int uid = getUidForPid(pid);
             String processName = readCmdlineFromProcfs(pid);
+            // Sometimes we get here processName that is not included in the whitelist. It comes
+            // from forking the zygote for an app. We can ignore that sample because this process
+            // is collected by ProcessMemoryState.
+            if (!processNames.contains(processName)) {
+                continue;
+            }
             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeInt(uid);
             e.writeString(processName);
             e.writeLong(memoryStat.pgfault);
             e.writeLong(memoryStat.pgmajfault);
             e.writeLong(memoryStat.rssInBytes);
-            e.writeLong(memoryStat.rssHighWatermarkInBytes);
+            e.writeLong(0);  // unused
             e.writeLong(memoryStat.startTimeNanos);
             pulledData.add(e);
         }
@@ -1517,6 +1524,21 @@
         pulledData.add(e);
     }
 
+    private void pullBuildInformation(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(Build.FINGERPRINT);
+        e.writeString(Build.BRAND);
+        e.writeString(Build.PRODUCT);
+        e.writeString(Build.DEVICE);
+        e.writeString(Build.VERSION.RELEASE);
+        e.writeString(Build.ID);
+        e.writeString(Build.VERSION.INCREMENTAL);
+        e.writeString(Build.TYPE);
+        e.writeString(Build.TAGS);
+        pulledData.add(e);
+    }
+
     private BatteryStatsHelper getBatteryStatsHelper() {
         if (mBatteryStatsHelper == null) {
             final long callingToken = Binder.clearCallingIdentity();
@@ -1638,6 +1660,11 @@
             return;
         }
         int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
+        if (cpuFrequencies.length != CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES) {
+            Slog.w(TAG, "Expected " + CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES
+                    + " frequencies, but got " + cpuFrequencies.length);
+            return;
+        }
         for (int i = 0; i < processCpuUsages.size(); i++) {
             KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
             ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
@@ -1651,23 +1678,18 @@
                     continue;
                 }
 
-                for (int k = 0; k < threadCpuUsage.usageTimesMillis.length; k++) {
-                    // Do not report CPU usage at a frequency when it's zero
-                    if (threadCpuUsage.usageTimesMillis[k] == 0) {
-                        continue;
-                    }
-
-                    StatsLogEventWrapper e =
-                            new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
-                    e.writeInt(processCpuUsage.uid);
-                    e.writeInt(processCpuUsage.processId);
-                    e.writeInt(threadCpuUsage.threadId);
-                    e.writeString(processCpuUsage.processName);
-                    e.writeString(threadCpuUsage.threadName);
+                StatsLogEventWrapper e =
+                        new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                e.writeInt(processCpuUsage.uid);
+                e.writeInt(processCpuUsage.processId);
+                e.writeInt(threadCpuUsage.threadId);
+                e.writeString(processCpuUsage.processName);
+                e.writeString(threadCpuUsage.threadName);
+                for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_NUM_FREQUENCIES; k++) {
                     e.writeInt(cpuFrequencies[k]);
                     e.writeInt(threadCpuUsage.usageTimesMillis[k]);
-                    pulledData.add(e);
                 }
+                pulledData.add(e);
             }
         }
     }
@@ -1810,6 +1832,10 @@
                 pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.BUILD_INFORMATION: {
+                pullBuildInformation(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             case StatsLog.PROCESS_CPU_TIME: {
                 pullProcessCpuTime(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 361622f..6e4c00e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -17,13 +17,17 @@
 package com.android.server.statusbar;
 
 import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.ActivityThread;
+import android.app.Notification;
 import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -39,7 +43,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
+import android.util.SparseArray;
 
 import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBar;
@@ -62,7 +66,7 @@
  * A note on locking:  We rely on the fact that calls onto mBar are oneway or
  * if they are local, that they just enqueue messages to not deadlock.
  */
-public class StatusBarManagerService extends IStatusBarService.Stub {
+public class StatusBarManagerService extends IStatusBarService.Stub implements DisplayListener {
     private static final String TAG = "StatusBarManagerService";
     private static final boolean SPEW = false;
 
@@ -78,24 +82,13 @@
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
     private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener;
     private IBinder mSysUiVisToken = new Binder();
-    private int mDisabled1 = 0;
-    private int mDisabled2 = 0;
 
     private final Object mLock = new Object();
     private final DeathRecipient mDeathRecipient = new DeathRecipient();
-    // encompasses lights-out mode and other flags defined on View
-    private int mSystemUiVisibility = 0;
-    private int mFullscreenStackSysUiVisibility;
-    private int mDockedStackSysUiVisibility;
-    private final Rect mFullscreenStackBounds = new Rect();
-    private final Rect mDockedStackBounds = new Rect();
-    private boolean mMenuVisible = false;
-    private int mImeWindowVis = 0;
-    private int mImeBackDisposition;
-    private boolean mShowImeSwitcher;
-    private IBinder mImeToken = null;
     private int mCurrentUserId;
 
+    private SparseArray<UiState> mDisplayUiState = new SparseArray<>();
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
             mBar.asBinder().unlinkToDeath(this,0);
@@ -184,8 +177,29 @@
 
         LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
         LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
+
+        // We always have a default display.
+        final UiState state = new UiState();
+        mDisplayUiState.put(DEFAULT_DISPLAY, state);
+
+        final DisplayManager displayManager =
+                (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+        displayManager.registerDisplayListener(this, mHandler);
     }
 
+    @Override
+    public void onDisplayAdded(int displayId) {}
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        synchronized (mLock) {
+            mDisplayUiState.remove(displayId);
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {}
+
     /**
      * Private API used by NotificationManagerService.
      */
@@ -239,22 +253,14 @@
 
         @Override
         public void topAppWindowChanged(int displayId, boolean menuVisible) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
-            StatusBarManagerService.this.topAppWindowChanged(menuVisible);
+            StatusBarManagerService.this.topAppWindowChanged(displayId, menuVisible);
         }
 
         @Override
         public void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
                 int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds,
                 String cause) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
-            StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
+            StatusBarManagerService.this.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
                     dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
         }
 
@@ -271,13 +277,9 @@
         @Override
         public void appTransitionFinished(int displayId) {
             enforceStatusBarService();
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionFinished();
+                    mBar.appTransitionFinished(displayId);
                 } catch (RemoteException ex) {}
             }
         }
@@ -373,39 +375,27 @@
 
         @Override
         public void setWindowState(int displayId, int window, int state) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.setWindowState(window, state);
+                    mBar.setWindowState(displayId, window, state);
                 } catch (RemoteException ex) {}
             }
         }
 
         @Override
         public void appTransitionPending(int displayId) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionPending();
+                    mBar.appTransitionPending(displayId);
                 } catch (RemoteException ex) {}
             }
         }
 
         @Override
         public void appTransitionCancelled(int displayId) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
-                    mBar.appTransitionCancelled();
+                    mBar.appTransitionCancelled(displayId);
                 } catch (RemoteException ex) {}
             }
         }
@@ -413,14 +403,10 @@
         @Override
         public void appTransitionStarting(int displayId, long statusBarAnimationsStartTime,
                 long statusBarAnimationsDuration) {
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                // TODO (b/117478341): Resolve one status bar/ navigation bar assumption
-                return;
-            }
             if (mBar != null) {
                 try {
                     mBar.appTransitionStarting(
-                            statusBarAnimationsStartTime, statusBarAnimationsDuration);
+                            displayId, statusBarAnimationsStartTime, statusBarAnimationsDuration);
                 } catch (RemoteException ex) {}
             }
         }
@@ -448,6 +434,7 @@
             return false;
         }
 
+        // TODO(b/118592525): support it per display if necessary.
         @Override
         public void onProposedRotationChanged(int rotation, boolean isValid) {
             if (mBar != null){
@@ -461,7 +448,9 @@
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
         @Override
         public boolean isGlobalActionsDisabled() {
-            return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+            // TODO(b/118592525): support global actions for multi-display.
+            final int disabled2 = mDisplayUiState.get(DEFAULT_DISPLAY).getDisabled2();
+            return (disabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
         }
 
         @Override
@@ -597,8 +586,8 @@
     }
 
     @Override
-    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type,
-            boolean requireConfirmation, int userId) {
+    public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
+            int type, boolean requireConfirmation, int userId) {
         enforceBiometricDialog();
         if (mBar != null) {
             try {
@@ -653,19 +642,33 @@
     }
 
     @Override
+    public void showBiometricTryAgain() {
+        enforceBiometricDialog();
+        if (mBar != null) {
+            try {
+                mBar.showBiometricTryAgain();
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
+    // TODO(b/117478341): make it aware of multi-display if needed.
+    @Override
     public void disable(int what, IBinder token, String pkg) {
         disableForUser(what, token, pkg, mCurrentUserId);
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     @Override
     public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
 
         synchronized (mLock) {
-            disableLocked(userId, what, token, pkg, 1);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
         }
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
      * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
      * To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -677,6 +680,7 @@
         disable2ForUser(what, token, pkg, mCurrentUserId);
     }
 
+    // TODO(b/117478341): make it aware of multi-display if needed.
     /**
      * Disable additional status bar features for a given user. Pass the bitwise-or of the
      * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
@@ -688,11 +692,12 @@
         enforceStatusBar();
 
         synchronized (mLock) {
-            disableLocked(userId, what, token, pkg, 2);
+            disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
         }
     }
 
-    private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
+    private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+            int whichFlag) {
         // It's important that the the callback and the call to mBar get done
         // in the same order when multiple threads are calling this function
         // so they are paired correctly.  The messages on the handler will be
@@ -711,22 +716,19 @@
                 disabledData += "    ([" + i + "] " + tok + "), ";
             }
             disabledData += " }";
-            Log.d(TAG, "disabledlocked (b/113914868): net1=" + net1 + ", mDisabled1=" + mDisabled1
-                    + ", token=" + token + ", mDisableRecords=" + mDisableRecords.size() + " => "
-                    + disabledData);
-        }
+            final UiState state = getUiState(displayId);
 
-        if (net1 != mDisabled1 || net2 != mDisabled2) {
-            mDisabled1 = net1;
-            mDisabled2 = net2;
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        mNotificationDelegate.onSetDisabled(net1);
-                    }
-                });
+            Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + "net1=" + net1
+                    + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
+                    + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
+        }
+        final UiState state = getUiState(displayId);
+        if (state.disableEquals(net1, net2)) {
+            state.setDisabled(net1, net2);
+            mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
             if (mBar != null) {
                 try {
-                    mBar.disable(net1, net2);
+                    mBar.disable(displayId, net1, net2);
                 } catch (RemoteException ex) {
                 }
             }
@@ -796,26 +798,27 @@
      * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
      * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
      */
-    private void topAppWindowChanged(final boolean menuVisible) {
+    private void topAppWindowChanged(int displayId, final boolean menuVisible) {
         enforceStatusBar();
 
-        if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
-
+        if (SPEW) {
+            Slog.d(TAG, "display#" + displayId + ": "
+                    + (menuVisible ? "showing" : "hiding") + " MENU key");
+        }
         synchronized(mLock) {
-            mMenuVisible = menuVisible;
-            mHandler.post(new Runnable() {
-                public void run() {
-                    if (mBar != null) {
-                        try {
-                            mBar.topAppWindowChanged(menuVisible);
-                        } catch (RemoteException ex) {
-                        }
+            getUiState(displayId).setMenuVisible(menuVisible);
+            mHandler.post(() -> {
+                if (mBar != null) {
+                    try {
+                        mBar.topAppWindowChanged(displayId, menuVisible);
+                    } catch (RemoteException ex) {
                     }
                 }
             });
         }
     }
 
+    // TODO(b/117478341): support back button change when IME is showing on a external display.
     @Override
     public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
             final boolean showImeSwitcher) {
@@ -829,39 +832,42 @@
             // In case of IME change, we need to call up setImeWindowStatus() regardless of
             // mImeWindowVis because mImeWindowVis may not have been set to false when the
             // previous IME was destroyed.
-            mImeWindowVis = vis;
-            mImeBackDisposition = backDisposition;
-            mImeToken = token;
-            mShowImeSwitcher = showImeSwitcher;
-            mHandler.post(new Runnable() {
-                public void run() {
-                    if (mBar != null) {
-                        try {
-                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
-                        } catch (RemoteException ex) {
-                        }
-                    }
-                }
+            // TODO(b/117478341): support back button change when IME is showing on a external
+            // display.
+            getUiState(DEFAULT_DISPLAY)
+                    .setImeWindowState(vis, backDisposition, showImeSwitcher, token);
+
+            mHandler.post(() -> {
+                if (mBar == null) return;
+                try {
+                    // TODO(b/117478341): support back button change when IME is showing on a
+                    // external display.
+                    mBar.setImeWindowStatus(
+                            DEFAULT_DISPLAY, token, vis, backDisposition, showImeSwitcher);
+                } catch (RemoteException ex) { }
             });
         }
     }
 
     @Override
-    public void setSystemUiVisibility(int vis, int mask, String cause) {
-        setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
+    public void setSystemUiVisibility(int displayId, int vis, int mask, String cause) {
+        final UiState state = getUiState(displayId);
+        setSystemUiVisibility(displayId, vis, 0, 0, mask,
+                state.mFullscreenStackBounds, state.mDockedStackBounds, cause);
     }
 
-    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
-            Rect fullscreenBounds, Rect dockedBounds, String cause) {
+    private void setSystemUiVisibility(int displayId, int vis, int fullscreenStackVis,
+            int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause) {
         // also allows calls from window manager which is in this process.
         enforceStatusBarService();
 
         if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
 
         synchronized (mLock) {
-            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
+            updateUiVisibilityLocked(displayId, vis, fullscreenStackVis, dockedStackVis, mask,
                     fullscreenBounds, dockedBounds);
             disableLocked(
+                    displayId,
                     mCurrentUserId,
                     vis & StatusBarManager.DISABLE_MASK,
                     mSysUiVisToken,
@@ -869,30 +875,107 @@
         }
     }
 
-    private void updateUiVisibilityLocked(final int vis,
+    private void updateUiVisibilityLocked(final int displayId, final int vis,
             final int fullscreenStackVis, final int dockedStackVis, final int mask,
             final Rect fullscreenBounds, final Rect dockedBounds) {
-        if (mSystemUiVisibility != vis
-                || mFullscreenStackSysUiVisibility != fullscreenStackVis
-                || mDockedStackSysUiVisibility != dockedStackVis
-                || !mFullscreenStackBounds.equals(fullscreenBounds)
-                || !mDockedStackBounds.equals(dockedBounds)) {
+        final UiState state = getUiState(displayId);
+        if (!state.systemUiStateEquals(vis, fullscreenStackVis, dockedStackVis,
+                fullscreenBounds, dockedBounds)) {
+            state.setSystemUiState(vis, fullscreenStackVis, dockedStackVis, fullscreenBounds,
+                    dockedBounds);
+            mHandler.post(() -> {
+                if (mBar != null) {
+                    try {
+                        mBar.setSystemUiVisibility(displayId, vis, fullscreenStackVis,
+                                dockedStackVis, mask, fullscreenBounds, dockedBounds);
+                    } catch (RemoteException ex) {
+                        Log.w(TAG, "Can not get StatusBar!");
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * @return {@link UiState} specified by {@code displayId}.
+     *
+     * <p>
+     *   Note: If {@link UiState} specified by {@code displayId} does not exist, {@link UiState}
+     *   will be allocated and {@code mDisplayUiState} will be updated accordingly.
+     * <p/>
+     */
+    private UiState getUiState(int displayId) {
+        UiState state = mDisplayUiState.get(displayId);
+        if (state == null) {
+            state = new UiState();
+            mDisplayUiState.put(displayId, state);
+        }
+        return state;
+    }
+
+    private class UiState {
+        private int mSystemUiVisibility = 0;
+        private int mFullscreenStackSysUiVisibility = 0;
+        private int mDockedStackSysUiVisibility = 0;
+        private final Rect mFullscreenStackBounds = new Rect();
+        private final Rect mDockedStackBounds = new Rect();
+        private boolean mMenuVisible = false;
+        private int mDisabled1 = 0;
+        private int mDisabled2 = 0;
+        private int mImeWindowVis = 0;
+        private int mImeBackDisposition = 0;
+        private boolean mShowImeSwitcher = false;
+        private IBinder mImeToken = null;
+
+        private int getDisabled1() {
+            return mDisabled1;
+        }
+
+        private int getDisabled2() {
+            return mDisabled2;
+        }
+
+        private void setDisabled(int disabled1, int disabled2) {
+            mDisabled1 = disabled1;
+            mDisabled2 = disabled2;
+        }
+
+        private boolean isMenuVisible() {
+            return mMenuVisible;
+        }
+
+        private void setMenuVisible(boolean menuVisible) {
+            mMenuVisible = menuVisible;
+        }
+
+        private boolean disableEquals(int disabled1, int disabled2) {
+            return mDisabled1 == disabled1 && mDisabled2 == disabled2;
+        }
+
+        private void setSystemUiState(final int vis, final int fullscreenStackVis,
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
             mSystemUiVisibility = vis;
             mFullscreenStackSysUiVisibility = fullscreenStackVis;
             mDockedStackSysUiVisibility = dockedStackVis;
             mFullscreenStackBounds.set(fullscreenBounds);
             mDockedStackBounds.set(dockedBounds);
-            mHandler.post(new Runnable() {
-                    public void run() {
-                        if (mBar != null) {
-                            try {
-                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
-                                        mask, fullscreenBounds, dockedBounds);
-                            } catch (RemoteException ex) {
-                            }
-                        }
-                    }
-                });
+        }
+
+        private boolean systemUiStateEquals(final int vis, final int fullscreenStackVis,
+                final int dockedStackVis, final Rect fullscreenBounds, final Rect dockedBounds) {
+            return mSystemUiVisibility == vis
+                && mFullscreenStackSysUiVisibility == fullscreenStackVis
+                && mDockedStackSysUiVisibility == dockedStackVis
+                && mFullscreenStackBounds.equals(fullscreenBounds)
+                && mDockedStackBounds.equals(dockedBounds);
+        }
+
+        private void setImeWindowState(final int vis, final int backDisposition,
+                final boolean showImeSwitcher, final IBinder token) {
+            mImeWindowVis = vis;
+            mImeBackDisposition = backDisposition;
+            mShowImeSwitcher = showImeSwitcher;
+            mImeToken = token;
         }
     }
 
@@ -927,6 +1010,7 @@
     // ================================================================================
     // Callbacks from the status bar service.
     // ================================================================================
+    // TODO(b/118592525): refactor it as an IStatusBar API.
     @Override
     public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
             List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
@@ -944,18 +1028,21 @@
             }
         }
         synchronized (mLock) {
+            // TODO(b/118592525): Currently, status bar only works on the default display.
+            // Make it aware of multi-display if needed.
+            final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
             switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
-            switches[1] = mSystemUiVisibility;
-            switches[2] = mMenuVisible ? 1 : 0;
-            switches[3] = mImeWindowVis;
-            switches[4] = mImeBackDisposition;
-            switches[5] = mShowImeSwitcher ? 1 : 0;
+            switches[1] = state.mSystemUiVisibility;
+            switches[2] = state.mMenuVisible ? 1 : 0;
+            switches[3] = state.mImeWindowVis;
+            switches[4] = state.mImeBackDisposition;
+            switches[5] = state.mShowImeSwitcher ? 1 : 0;
             switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
-            switches[7] = mFullscreenStackSysUiVisibility;
-            switches[8] = mDockedStackSysUiVisibility;
-            binders.add(mImeToken);
-            fullscreenStackBounds.set(mFullscreenStackBounds);
-            dockedStackBounds.set(mDockedStackBounds);
+            switches[7] = state.mFullscreenStackSysUiVisibility;
+            switches[8] = state.mDockedStackSysUiVisibility;
+            binders.add(state.mImeToken);
+            fullscreenStackBounds.set(state.mFullscreenStackBounds);
+            dockedStackBounds.set(state.mDockedStackBounds);
         }
     }
 
@@ -1080,14 +1167,16 @@
     }
 
     @Override
-    public void onNotificationActionClick(String key, int actionIndex, NotificationVisibility nv) {
+    public void onNotificationActionClick(
+            String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
+            boolean generatedByAssistant) {
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
-                    actionIndex, nv);
+                    actionIndex, action, nv, generatedByAssistant);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1295,8 +1384,13 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
         synchronized (mLock) {
-            pw.println("  mDisabled1=0x" + Integer.toHexString(mDisabled1));
-            pw.println("  mDisabled2=0x" + Integer.toHexString(mDisabled2));
+            for (int i = 0; i < mDisplayUiState.size(); i++) {
+                final int key = mDisplayUiState.keyAt(i);
+                final UiState state = mDisplayUiState.get(key);
+                pw.println("  displayId=" + key);
+                pw.println("    mDisabled1=0x" + Integer.toHexString(state.getDisabled1()));
+                pw.println("    mDisabled2=0x" + Integer.toHexString(state.getDisabled2()));
+            }
             final int N = mDisableRecords.size();
             pw.println("  mDisableRecords.size=" + N);
             for (int i=0; i<N; i++) {
diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java
index 6a0b648..9d6a647 100644
--- a/services/core/java/com/android/server/storage/AppFuseBridge.java
+++ b/services/core/java/com/android/server/storage/AppFuseBridge.java
@@ -16,6 +16,7 @@
 
 package com.android.server.storage;
 
+import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -25,8 +26,6 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.NativeDaemonConnectorException;
 import libcore.io.IoUtils;
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -87,7 +86,7 @@
         }
     }
 
-    public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode)
+    public ParcelFileDescriptor openFile(int mountId, int fileId, int mode)
             throws FuseUnavailableMountException, InterruptedException {
         final MountScope scope;
         synchronized (this) {
@@ -96,17 +95,14 @@
                 throw new FuseUnavailableMountException(mountId);
             }
         }
-        if (scope.pid != pid) {
-            throw new SecurityException("PID does not match");
-        }
         final boolean result = scope.waitForMount();
         if (result == false) {
             throw new FuseUnavailableMountException(mountId);
         }
         try {
-            return ParcelFileDescriptor.open(
-                    new File(scope.mountPoint, String.valueOf(fileId)), mode);
-        } catch (FileNotFoundException error) {
+            int flags = FileUtils.translateModePfdToPosix(mode);
+            return scope.openFile(mountId, fileId, flags);
+        } catch (NativeDaemonConnectorException error) {
             throw new FuseUnavailableMountException(mountId);
         }
     }
@@ -131,17 +127,13 @@
 
     public static abstract class MountScope implements AutoCloseable {
         public final int uid;
-        public final int pid;
         public final int mountId;
-        public final File mountPoint;
         private final CountDownLatch mMounted = new CountDownLatch(1);
         private boolean mMountResult = false;
 
-        public MountScope(int uid, int pid, int mountId) {
+        public MountScope(int uid, int mountId) {
             this.uid = uid;
-            this.pid = pid;
             this.mountId = mountId;
-            this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE,  uid, mountId));
         }
 
         @GuardedBy("AppFuseBridge.this")
@@ -159,6 +151,8 @@
         }
 
         public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
+        public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags)
+                throws NativeDaemonConnectorException;
     }
 
     private native long native_new();
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 8d27d1e..c8a68b4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -402,7 +402,7 @@
             throws RemoteException {
         try {
             final int uid = context.getPackageManager()
-                    .getPackageUid(packageName, 0);
+                    .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
             Preconditions.checkArgument(Binder.getCallingUid() == uid);
         } catch (IllegalArgumentException | NullPointerException |
                 PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index da0a794..4b413e5 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -49,7 +49,7 @@
 import libcore.icu.ICU;
 import libcore.timezone.TzDataSetVersion;
 import libcore.timezone.TimeZoneFinder;
-import libcore.util.ZoneInfoDB;
+import libcore.timezone.ZoneInfoDB;
 
 import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
 import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8..1163d39 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -626,7 +626,7 @@
         updateServiceConnectionLocked(serviceState.component, userId);
     }
 
-    private void createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
+    private boolean createSessionInternalLocked(ITvInputService service, IBinder sessionToken,
             int userId) {
         UserState userState = getOrCreateUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
@@ -638,6 +638,7 @@
         // Set up a callback to send the session token.
         ITvInputSessionCallback callback = new SessionCallback(sessionState, channels);
 
+        boolean created = true;
         // Create a session. When failed, send a null token immediately.
         try {
             if (sessionState.isRecordingSession) {
@@ -647,11 +648,12 @@
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
-            removeSessionStateLocked(sessionToken, userId);
             sendSessionTokenToClientLocked(sessionState.client, sessionState.inputId, null,
                     null, sessionState.seq);
+            created = false;
         }
         channels[1].dispose();
+        return created;
     }
 
     private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
@@ -1193,8 +1195,10 @@
                     serviceState.sessionTokens.add(sessionToken);
 
                     if (serviceState.service != null) {
-                        createSessionInternalLocked(serviceState.service, sessionToken,
-                                resolvedUserId);
+                        if (!createSessionInternalLocked(serviceState.service, sessionToken,
+                                resolvedUserId)) {
+                            removeSessionStateLocked(sessionToken, resolvedUserId);
+                        }
                     } else {
                         updateServiceConnectionLocked(info.getComponent(), resolvedUserId);
                     }
@@ -2282,9 +2286,17 @@
                     }
                 }
 
+                List<IBinder> tokensToBeRemoved = new ArrayList<>();
+
                 // And create sessions, if any.
                 for (IBinder sessionToken : serviceState.sessionTokens) {
-                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+                    if (!createSessionInternalLocked(serviceState.service, sessionToken, mUserId)) {
+                        tokensToBeRemoved.add(sessionToken);
+                    }
+                }
+
+                for (IBinder sessionToken : tokensToBeRemoved) {
+                    removeSessionStateLocked(sessionToken, mUserId);
                 }
 
                 for (TvInputState inputState : userState.inputMap.values()) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6ede423..64ff9cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1181,7 +1181,7 @@
                 // TODO(multi-display) TBD.
                 if (mInfo != null && mInfo.supportsAmbientMode() && displayId == DEFAULT_DISPLAY) {
                     try {
-                        connector.mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
+                        connector.mEngine.setInAmbientMode(mInAmbientMode, 0L /* duration */);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to set ambient mode state", e);
                     }
@@ -2023,11 +2023,17 @@
         }
     }
 
-    // TODO(b/115486823) Extends this method with specific display.
-    public void setInAmbientMode(boolean inAmbienMode, boolean animated) {
+    /**
+     * TODO(b/115486823) Extends this method with specific display.
+     * Propagate ambient state to wallpaper engine.
+     *
+     * @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
+     * @param animationDuration Duration of the animation, or 0 when immediate.
+     */
+    public void setInAmbientMode(boolean inAmbientMode, long animationDuration) {
         final IWallpaperEngine engine;
         synchronized (mLock) {
-            mInAmbientMode = inAmbienMode;
+            mInAmbientMode = inAmbientMode;
             final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
             if (data != null && data.connection != null && data.connection.mInfo != null
                     && data.connection.mInfo.supportsAmbientMode()) {
@@ -2040,7 +2046,7 @@
 
         if (engine != null) {
             try {
-                engine.setInAmbientMode(inAmbienMode, animated);
+                engine.setInAmbientMode(inAmbientMode, animationDuration);
             } catch (RemoteException e) {
                 // Cannot talk to wallpaper engine.
             }
@@ -2344,7 +2350,7 @@
                 return false;
             }
             if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
-                String msg = "Selected service does not require "
+                String msg = "Selected service does not have "
                         + android.Manifest.permission.BIND_WALLPAPER
                         + ": " + componentName;
                 if (fromUser) {
@@ -2396,6 +2402,22 @@
                 }
             }
 
+            if (wi != null && wi.supportsAmbientMode()) {
+                final int hasPrivilege = mIPackageManager.checkPermission(
+                        android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
+                        serviceUserId);
+                if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+                    String msg = "Selected service does not have "
+                            + android.Manifest.permission.AMBIENT_WALLPAPER
+                            + ": " + componentName;
+                    if (fromUser) {
+                        throw new SecurityException(msg);
+                    }
+                    Slog.w(TAG, msg);
+                    return false;
+                }
+            }
+
             // Bind the service!
             if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
             final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
@@ -2800,8 +2822,6 @@
         IoUtils.closeQuietly(stream);
 
         if (!success) {
-            wpdData.mWidth = -1;
-            wpdData.mHeight = -1;
             wallpaper.cropHint.set(0, 0, 0, 0);
             wpdData.mPadding.set(0, 0, 0, 0);
             wallpaper.name = "";
@@ -2817,6 +2837,7 @@
             }
         }
 
+        ensureSaneWallpaperDisplaySize(wpdData, DEFAULT_DISPLAY);
         ensureSaneWallpaperData(wallpaper, DEFAULT_DISPLAY);
         WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
         if (lockWallpaper != null) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e4d1cfe..fe0b5c2 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,9 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -706,7 +706,7 @@
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
                         surfaceControl = mService.getDefaultDisplayContentLocked().makeOverlay()
                                 .setName(SURFACE_TITLE)
-                                .setSize(mTempPoint.x, mTempPoint.y) // not a typo
+                                .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo
                                 .setFormat(PixelFormat.TRANSLUCENT)
                                 .build();
                     } catch (OutOfResourcesException oore) {
@@ -784,7 +784,7 @@
                 public void updateSize() {
                     synchronized (mService.mGlobalLock) {
                         mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                        mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+                        mSurfaceControl.setBufferSize(mTempPoint.x, mTempPoint.y);
                         invalidate(mDirtyRect);
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index ed36645..7bf2c94 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -39,8 +39,6 @@
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
@@ -48,6 +46,8 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
@@ -84,7 +84,8 @@
      */
     private static int sNextFreeStackId = 0;
 
-    private ActivityStackSupervisor mSupervisor;
+    private ActivityTaskManagerService mService;
+    private RootActivityContainer mRootActivityContainer;
     /** Actual Display this object tracks. */
     int mDisplayId;
     Display mDisplay;
@@ -141,8 +142,9 @@
 
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
-    ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-        mSupervisor = supervisor;
+    ActivityDisplay(RootActivityContainer root, Display display) {
+        mRootActivityContainer = root;
+        mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
         mWindowContainerController = createWindowContainerController();
@@ -168,7 +170,7 @@
         if (displayId != DEFAULT_DISPLAY) {
             final int displayState = mDisplay.getState();
             if (displayState == Display.STATE_OFF && mOffToken == null) {
-                mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId);
+                mOffToken = mService.acquireSleepToken("Display-off", displayId);
             } else if (displayState == Display.STATE_ON && mOffToken != null) {
                 mOffToken.release();
                 mOffToken = null;
@@ -179,6 +181,11 @@
         mWindowContainerController.onDisplayChanged();
     }
 
+    @Override
+    public void onInitializeOverrideConfiguration(Configuration config) {
+        getOverrideConfiguration().updateFrom(config);
+    }
+
     void addChild(ActivityStack stack, int position) {
         if (position == POSITION_BOTTOM) {
             position = 0;
@@ -189,7 +196,7 @@
                 + " to displayId=" + mDisplayId + " position=" + position);
         addStackReferenceIfNeeded(stack);
         positionChildAt(stack, position);
-        mSupervisor.mService.updateSleepIfNeededLocked();
+        mService.updateSleepIfNeededLocked();
     }
 
     void removeChild(ActivityStack stack) {
@@ -201,7 +208,7 @@
         }
         removeStackReferenceIfNeeded(stack);
         releaseSelfIfNeeded();
-        mSupervisor.mService.updateSleepIfNeededLocked();
+        mService.updateSleepIfNeededLocked();
         onStackOrderChanged();
     }
 
@@ -252,7 +259,7 @@
             final ActivityStack currentFocusedStack = getFocusedStack();
             if (currentFocusedStack != prevFocusedStack) {
                 mLastFocusedStack = prevFocusedStack;
-                EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId,
+                EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId,
                         currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(),
                         mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(),
                         updateLastFocusedStackReason);
@@ -409,10 +416,10 @@
             }
         }
 
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow,
-                service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement,
-                service.mSupportsPictureInPicture, activityType)) {
+        if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
+                mService.mSupportsSplitScreenMultiWindow,
+                mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
+                activityType)) {
             throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
                     + windowingMode);
         }
@@ -425,10 +432,12 @@
     <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop) {
         if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop);
+            return (T) new PinnedActivityStack(this, stackId,
+                    mRootActivityContainer.mStackSupervisor, onTop);
         }
-        return (T) new ActivityStack(
-                        this, stackId, mSupervisor, windowingMode, activityType, onTop);
+        return (T) new ActivityStack(this, stackId,
+                mRootActivityContainer.mStackSupervisor, windowingMode, activityType,
+                onTop);
     }
 
     /**
@@ -543,7 +552,7 @@
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             // TODO(b/111541062): Check if resumed activity on this display instead
-            if (!mSupervisor.isTopDisplayFocusedStack(stack)
+            if (!mRootActivityContainer.isTopDisplayFocusedStack(stack)
                     && stack.getResumedActivity() != null) {
                 if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
                         " mResumedActivity=" + stack.getResumedActivity());
@@ -608,7 +617,7 @@
                 if (stack.getWindowingMode() != windowingMode) {
                     continue;
                 }
-                mSupervisor.removeStack(stack);
+                mRootActivityContainer.mStackSupervisor.removeStack(stack);
             }
         }
     }
@@ -623,7 +632,7 @@
             for (int i = mStacks.size() - 1; i >= 0; --i) {
                 final ActivityStack stack = mStacks.get(i);
                 if (stack.getActivityType() == activityType) {
-                    mSupervisor.removeStack(stack);
+                    mRootActivityContainer.mStackSupervisor.removeStack(stack);
                 }
             }
         }
@@ -685,7 +694,7 @@
     }
 
     private void onSplitScreenModeDismissed() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
+        mRootActivityContainer.mWindowManager.deferSurfaceLayout();
         try {
             // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
             for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -709,12 +718,12 @@
                 mHomeStack.moveToFront("onSplitScreenModeDismissed");
                 topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
             }
-            mSupervisor.mWindowManager.continueSurfaceLayout();
+            mRootActivityContainer.mWindowManager.continueSurfaceLayout();
         }
     }
 
     private void onSplitScreenModeActivated() {
-        mSupervisor.mWindowManager.deferSurfaceLayout();
+        mRootActivityContainer.mWindowManager.deferSurfaceLayout();
         try {
             // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
             for (int i = mStacks.size() - 1; i >= 0; --i) {
@@ -729,7 +738,7 @@
                         false /* creating */);
             }
         } finally {
-            mSupervisor.mWindowManager.continueSurfaceLayout();
+            mRootActivityContainer.mWindowManager.continueSurfaceLayout();
         }
     }
 
@@ -824,11 +833,10 @@
     int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
         @Nullable TaskRecord task, int activityType) {
         // Make sure the windowing mode we are trying to use makes sense for what is supported.
-        final ActivityTaskManagerService service = mSupervisor.mService;
-        boolean supportsMultiWindow = service.mSupportsMultiWindow;
-        boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow;
-        boolean supportsFreeform = service.mSupportsFreeformWindowManagement;
-        boolean supportsPip = service.mSupportsPictureInPicture;
+        boolean supportsMultiWindow = mService.mSupportsMultiWindow;
+        boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
+        boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
+        boolean supportsPip = mService.mSupportsPictureInPicture;
         if (supportsMultiWindow) {
             if (task != null) {
                 supportsMultiWindow = task.isResizeable();
@@ -932,7 +940,7 @@
         // This activity can be considered the top running activity if we are not considering
         // the locked state, the keyguard isn't locked, or we can show when locked.
         if (topRunning != null && considerKeyguardState
-                && mSupervisor.getKeyguardController().isKeyguardLocked()
+                && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked()
                 && !topRunning.canShowWhenLocked()) {
             return null;
         }
@@ -1010,7 +1018,7 @@
 
     @Override
     protected ConfigurationContainer getParent() {
-        return mSupervisor;
+        return mRootActivityContainer;
     }
 
     boolean isPrivate() {
@@ -1043,8 +1051,8 @@
         // released (no more ActivityStack). But, we cannot release it at that moment or the
         // related WindowContainer and WindowContainerController will also be removed. So, we
         // set display as removed after reparenting stack finished.
-        final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay();
-        mSupervisor.beginDeferResume();
+        final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
+        mRootActivityContainer.mStackSupervisor.beginDeferResume();
         try {
             int numStacks = mStacks.size();
             // Keep the order from bottom to top.
@@ -1070,7 +1078,7 @@
                 numStacks = mStacks.size();
             }
         } finally {
-            mSupervisor.endDeferResume();
+            mRootActivityContainer.mStackSupervisor.endDeferResume();
         }
         mRemoved = true;
 
@@ -1082,9 +1090,9 @@
         releaseSelfIfNeeded();
 
         if (!mAllSleepTokens.isEmpty()) {
-            mSupervisor.mSleepTokens.removeAll(mAllSleepTokens);
+            mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
             mAllSleepTokens.clear();
-            mSupervisor.mService.updateSleepIfNeededLocked();
+            mService.updateSleepIfNeededLocked();
         }
     }
 
@@ -1092,8 +1100,9 @@
         if (mStacks.isEmpty() && mRemoved) {
             mWindowContainerController.removeContainer();
             mWindowContainerController = null;
-            mSupervisor.removeChild(this);
-            mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
+            mRootActivityContainer.removeChild(this);
+            mRootActivityContainer.mStackSupervisor
+                    .getKeyguardController().onDisplayRemoved(mDisplayId);
         }
     }
 
@@ -1122,7 +1131,7 @@
 
     boolean shouldSleep() {
         return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
-                && (mSupervisor.mService.mRunningVoice == null);
+                && (mService.mRunningVoice == null);
     }
 
     void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
@@ -1213,7 +1222,7 @@
 
     @Nullable
     ActivityRecord getHomeActivity() {
-        return getHomeActivityForUser(mSupervisor.mCurrentUser);
+        return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
     }
 
     @Nullable
@@ -1233,7 +1242,7 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.isActivityTypeHome()
-                        && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+                        && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
                     return r;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index e3133ef..eff0f75 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -42,7 +42,7 @@
  * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
  * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
  *
- * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
  * necessarily the same within a single launch sequence: it is only the top-most activity at the
  * time (if any). Trampoline activities coalesce several activity starts into a single launch
  * sequence.
@@ -94,6 +94,14 @@
     public static final int TEMPERATURE_HOT = 3;
 
     /**
+     * Typedef marker that a {@code byte[]} actually contains an
+     * <a href="proto/android/server/activitymanagerservice.proto">ActivityRecordProto</a>
+     * in the protobuf format.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ActivityRecordProto {}
+
+    /**
      * Notifies the observer that a new launch sequence has begun as a result of a new intent.
      *
      * Once a launch sequence begins, the resolved activity will either subsequently start with
@@ -135,7 +143,7 @@
      * Multiple calls to this method cannot occur without first terminating the current
      * launch sequence.
      */
-    public void onActivityLaunched(@NonNull ActivityRecord activity,
+    public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
                                    @Temperature int temperature);
 
     /**
@@ -157,7 +165,7 @@
      *          in the case of a trampoline, multiple activities could've been started
      *          and only the latest activity is reported here.
      */
-    public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+    public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] abortingActivity);
 
     /**
      * Notifies the observer that the current launch sequence has been successfully finished.
@@ -178,5 +186,5 @@
      *          and only the latest activity that was top-most during first-frame drawn
      *          is reported here.
      */
-    public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+    public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
new file mode 100644
index 0000000..fa90dc5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserverRegistry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import android.annotation.NonNull;
+
+/**
+ * Multi-cast delegate implementation for {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br/><br/>
+ * This enables multiple launch observers to subscribe to {@link ActivityMetricsLogger}
+ * independently of each other.
+ *
+ * <br/><br/>
+ * Some callbacks in {@link ActivityMetricsLaunchObserver} have a {@code byte[]}
+ * parameter; this array is reused by all the registered observers, so it must not be written to
+ * (i.e. all observers must treat any array parameters as immutable).
+ *
+ * <br /><br />
+ * Multi-cast invocations occurs sequentially in-order of registered observers.
+ */
+public interface ActivityMetricsLaunchObserverRegistry {
+    /**
+     * Register an extra launch observer to receive the multi-cast.
+     *
+     * <br /><br />
+     * Multi-cast invocation happens in the same order the observers were registered. For example,
+     * <pre>
+     *     registerLaunchObserver(A)
+     *     registerLaunchObserver(B)
+     *
+     *     obs.onIntentFailed() ->
+     *       A.onIntentFailed()
+     *       B.onIntentFailed()
+     * </pre>
+     */
+    void registerLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+
+    /**
+     * Unregister an existing launch observer. It will not receive the multi-cast in the future.
+     *
+     * <br /><br />
+     * This does nothing if this observer was not already registered.
+     */
+    void unregisterLaunchObserver(@NonNull ActivityMetricsLaunchObserver launchObserver);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 1c08d03..12690a9 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -99,10 +99,12 @@
 import android.util.SparseIntArray;
 import android.util.StatsLog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
 /**
@@ -168,7 +170,8 @@
      * Due to the global single concurrent launch sequence, all calls to this observer must be made
      * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
      */
-    private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+    private final LaunchObserverRegistryImpl mLaunchObserver;
+    @VisibleForTesting static final int LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE = 512;
 
     private final class H extends Handler {
 
@@ -251,7 +254,7 @@
             type = getTransitionType(info);
             processRecord = findProcessForActivity(launchedActivity);
             processName = launchedActivity.processName;
-            userId = launchedActivity.userId;
+            userId = launchedActivity.mUserId;
             launchedActivityShortComponentName = launchedActivity.shortComponentName;
             activityRecordIdHashCode = System.identityHashCode(launchedActivity);
             this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
@@ -263,6 +266,7 @@
         mSupervisor = supervisor;
         mContext = context;
         mHandler = new H(looper);
+        mLaunchObserver = new LaunchObserverRegistryImpl(looper);
     }
 
     void logWindowState() {
@@ -277,7 +281,8 @@
         mLastLogTimeSecs = now;
 
         mWindowState = WINDOW_STATE_INVALID;
-        ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        ActivityStack stack =
+                mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack();
         if (stack == null) {
             return;
         }
@@ -289,7 +294,7 @@
 
         @WindowingMode int windowingMode = stack.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_PINNED) {
-            stack = mSupervisor.findStackBehind(stack);
+            stack = mSupervisor.mRootActivityContainer.findStackBehind(stack);
             windowingMode = stack.getWindowingMode();
         }
         switch (windowingMode) {
@@ -515,7 +520,7 @@
         if (info.launchedActivity != activityRecord) {
             return;
         }
-        final TaskRecord t = activityRecord.getTask();
+        final TaskRecord t = activityRecord.getTaskRecord();
         final SomeArgs args = SomeArgs.obtain();
         args.arg1 = t;
         args.arg2 = activityRecord;
@@ -850,9 +855,10 @@
         builder.addTaggedData(FIELD_TARGET_UID_HAS_ANY_VISIBLE_WINDOW,
                 targetUidHasAnyVisibleWindow ? 1 : 0);
         builder.addTaggedData(FIELD_TARGET_WHITELIST_TAG, targetWhitelistTag);
-        builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
         builder.addTaggedData(FIELD_COMING_FROM_PENDING_INTENT, comingFromPendingIntent ? 1 : 0);
-        builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        if (intent != null) {
+            builder.addTaggedData(FIELD_INTENT_ACTION, intent.getAction());
+        }
         if (callerApp != null) {
             builder.addTaggedData(FIELD_PROCESS_RECORD_PROCESS_NAME, callerApp.mName);
             builder.addTaggedData(FIELD_PROCESS_RECORD_CUR_PROC_STATE,
@@ -881,29 +887,34 @@
                         (nowUptime - callerApp.getWhenUnimportant()));
             }
         }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY, r.realActivity.toShortString());
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
-        if (r.lastVisibleTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
-                    (nowUptime - r.lastVisibleTime));
-        }
-        if (r.resultTo != null) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME, r.resultTo.packageName);
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
-                    r.resultTo.shortComponentName);
-        }
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
-        builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
-                r.visibleIgnoringKeyguard ? 1 : 0);
-        if (r.lastLaunchTime != 0) {
-            builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
-                    (nowUptime - r.lastLaunchTime));
+        if (r != null) {
+            builder.addTaggedData(FIELD_TARGET_SHORT_COMPONENT_NAME, r.shortComponentName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_LAUNCH_MODE, r.info.launchMode);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_TARGET_ACTIVITY, r.info.targetActivity);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_FLAGS, r.info.flags);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_REAL_ACTIVITY,
+                    r.mActivityComponent.toShortString());
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_SHORT_COMPONENT_NAME, r.shortComponentName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_PROCESS_NAME, r.processName);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_FULLSCREEN, r.fullscreen ? 1 : 0);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_NO_DISPLAY, r.noDisplay ? 1 : 0);
+            if (r.lastVisibleTime != 0) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_VISIBLE,
+                        (nowUptime - r.lastVisibleTime));
+            }
+            if (r.resultTo != null) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_PKG_NAME,
+                        r.resultTo.packageName);
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_RESULT_TO_SHORT_COMPONENT_NAME,
+                        r.resultTo.shortComponentName);
+            }
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE, r.visible ? 1 : 0);
+            builder.addTaggedData(FIELD_ACTIVITY_RECORD_IS_VISIBLE_IGNORING_KEYGUARD,
+                    r.visibleIgnoringKeyguard ? 1 : 0);
+            if (r.lastLaunchTime != 0) {
+                builder.addTaggedData(FIELD_ACTIVITY_RECORD_MILLIS_SINCE_LAST_LAUNCH,
+                        (nowUptime - r.lastLaunchTime));
+            }
         }
         mMetricsLogger.write(builder);
     }
@@ -993,12 +1004,19 @@
         }
     }
 
+    public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+        return mLaunchObserver;
+    }
+
     /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
     private void launchObserverNotifyIntentStarted(Intent intent) {
-        if (mLaunchObserver != null) {
-            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
-            mLaunchObserver.onIntentStarted(intent);
-        }
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:launchObserverNotifyIntentStarted");
+
+        // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+        mLaunchObserver.onIntentStarted(intent);
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     /**
@@ -1007,9 +1025,12 @@
      * intent being delivered to the top running activity.
      */
     private void launchObserverNotifyIntentFailed() {
-        if (mLaunchObserver != null) {
-            mLaunchObserver.onIntentFailed();
-        }
+       Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:launchObserverNotifyIntentFailed");
+
+        mLaunchObserver.onIntentFailed();
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     /**
@@ -1017,14 +1038,17 @@
      * has started.
      */
     private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:launchObserverNotifyActivityLaunched");
+
         @ActivityMetricsLaunchObserver.Temperature int temperature =
                 convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
 
-        if (mLaunchObserver != null) {
-            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
-            mLaunchObserver.onActivityLaunched(info.launchedActivity,
-                                               temperature);
-        }
+        // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+        mLaunchObserver.onActivityLaunched(convertActivityRecordToProto(info.launchedActivity),
+                                           temperature);
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     /**
@@ -1032,11 +1056,15 @@
      * cancelled.
      */
     private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
-        final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
 
-        if (mLaunchObserver != null) {
-            mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
-        }
+        final @ActivityMetricsLaunchObserver.ActivityRecordProto byte[] activityRecordProto =
+                info != null ? convertActivityRecordToProto(info.launchedActivity) : null;
+
+        mLaunchObserver.onActivityLaunchCancelled(activityRecordProto);
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     /**
@@ -1044,11 +1072,34 @@
      * has fully finished (successfully).
      */
     private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
-        final ActivityRecord launchedActivity = info.launchedActivity;
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
 
-        if (mLaunchObserver != null) {
-            mLaunchObserver.onActivityLaunchFinished(launchedActivity);
-        }
+        mLaunchObserver.onActivityLaunchFinished(
+                convertActivityRecordToProto(info.launchedActivity));
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    @VisibleForTesting
+    static @ActivityMetricsLaunchObserver.ActivityRecordProto byte[]
+            convertActivityRecordToProto(ActivityRecord record) {
+        // May take non-negligible amount of time to convert ActivityRecord into a proto,
+        // so track the time.
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "MetricsLogger:convertActivityRecordToProto");
+
+        // There does not appear to be a way to 'reset' a ProtoOutputBuffer stream,
+        // so create a new one every time.
+        final ProtoOutputStream protoOutputStream =
+                new ProtoOutputStream(LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+        // Write this data out as the top-most ActivityRecordProto (i.e. it is not a sub-object).
+        record.writeToProto(protoOutputStream);
+        final byte[] bytes = protoOutputStream.getBytes();
+
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+        return bytes;
     }
 
     private static @ActivityMetricsLaunchObserver.Temperature int
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5e92b9e..255a003 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,17 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
+import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
+import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
+import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
+import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -63,6 +73,7 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
 import static android.content.res.Configuration.EMPTY;
@@ -111,12 +122,18 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
-import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService
+        .RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.END_TAG;
@@ -147,6 +164,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.Build;
@@ -167,11 +185,14 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.AppTransitionAnimationSpec;
+import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.XmlUtils;
@@ -200,7 +221,7 @@
 /**
  * An entry in the history stack, representing an activity.
  */
-final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
+final class ActivityRecord extends ConfigurationContainer {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE;
@@ -223,18 +244,20 @@
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
 
-    final ActivityTaskManagerService service; // owner
+    final ActivityTaskManagerService mAtmService; // owner
     final IApplicationToken.Stub appToken; // window manager token
-    AppWindowContainerController mWindowContainerController;
+    // TODO: Remove after unification
+    AppWindowToken mAppWindowToken;
+
     final ActivityInfo info; // all about me
     // TODO: This is duplicated state already contained in info.applicationInfo - remove
     ApplicationInfo appInfo; // information about activity's app
     final int launchedFromPid; // always the pid who started the activity.
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
-    final int userId;          // Which user is this running for?
+    final int mUserId;          // Which user is this running for?
     final Intent intent;    // the original intent that generated us
-    final ComponentName realActivity;  // the intent component, or target of an alias.
+    final ComponentName mActivityComponent;  // the intent component, or target of an alias.
     final String shortComponentName; // the short component name of the intent
     final String resolvedType; // as per original caller;
     final String packageName; // the package implementing intent's component
@@ -322,6 +345,7 @@
 
     private boolean inHistory;  // are we in the history stack?
     final ActivityStackSupervisor mStackSupervisor;
+    final RootActivityContainer mRootActivityContainer;
 
     static final int STARTING_WINDOW_NOT_SHOWN = 0;
     static final int STARTING_WINDOW_SHOWN = 1;
@@ -383,14 +407,14 @@
                 pw.print(" processName="); pw.println(processName);
         pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
                 pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
-                pw.print(" userId="); pw.println(userId);
+                pw.print(" userId="); pw.println(mUserId);
         pw.print(prefix); pw.print("app="); pw.println(app);
         pw.print(prefix); pw.println(intent.toInsecureStringWithClip());
         pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask);
                 pw.print(" task="); pw.println(task);
         pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
-        pw.print(prefix); pw.print("realActivity=");
-                pw.println(realActivity.flattenToShortString());
+        pw.print(prefix); pw.print("mActivityComponent=");
+                pw.println(mActivityComponent.flattenToShortString());
         if (appInfo != null) {
             pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
             if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
@@ -620,7 +644,7 @@
                     "Reporting activity moved to display" + ", activityRecord=" + this
                             + ", displayId=" + displayId + ", config=" + config);
 
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     MoveToDisplayItem.obtain(displayId, config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -638,7 +662,7 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
                     + config);
 
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     ActivityConfigurationChangeItem.obtain(config));
         } catch (RemoteException e) {
             // If process died, whatever.
@@ -665,7 +689,7 @@
 
     private void scheduleMultiWindowModeChanged(Configuration overrideConfig) {
         try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     MultiWindowModeChangeItem.obtain(mLastReportedMultiWindowMode, overrideConfig));
         } catch (Exception e) {
             // If process died, I don't care.
@@ -696,7 +720,7 @@
 
     private void schedulePictureInPictureModeChanged(Configuration overrideConfig) {
         try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     PipModeChangeItem.obtain(mLastReportedPictureInPictureMode,
                             overrideConfig));
         } catch (Exception e) {
@@ -717,10 +741,10 @@
 
     @Override
     protected ConfigurationContainer getParent() {
-        return getTask();
+        return getTaskRecord();
     }
 
-    TaskRecord getTask() {
+    TaskRecord getTaskRecord() {
         return task;
     }
 
@@ -741,12 +765,12 @@
      * @param reparenting   Whether we're in the middle of reparenting.
      */
     void setTask(TaskRecord task, boolean reparenting) {
-        // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
-        if (task != null && task == getTask()) {
+        // Do nothing if the {@link TaskRecord} is the same as the current {@link getTaskRecord}.
+        if (task != null && task == getTaskRecord()) {
             return;
         }
 
-        final ActivityStack oldStack = getStack();
+        final ActivityStack oldStack = getActivityStack();
         final ActivityStack newStack = task != null ? task.getStack() : null;
 
         // Inform old stack (if present) of activity removal and new stack (if set) of activity
@@ -769,10 +793,16 @@
     }
 
     /**
-     * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)}
+     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
+     * This information helps AWT know that the app is in the process of pausing before it gets the
+     * signal on the WM side.
      */
     void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip);
+        if (mAppWindowToken == null) {
+            return;
+        }
+
+        mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip);
     }
 
     static class Token extends IApplicationToken.Stub {
@@ -789,7 +819,7 @@
                 return null;
             }
             ActivityRecord r = token.weakActivity.get();
-            if (r == null || r.getStack() == null) {
+            if (r == null || r.getActivityStack() == null) {
                 return null;
             }
             return r;
@@ -822,7 +852,7 @@
     }
 
     boolean isResolverActivity() {
-        return ResolverActivity.class.getName().equals(realActivity.getClassName());
+        return ResolverActivity.class.getName().equals(mActivityComponent.getClassName());
     }
 
     boolean isResolverOrChildActivity() {
@@ -831,7 +861,7 @@
         }
         try {
             return ResolverActivity.class.isAssignableFrom(
-                    Object.class.getClassLoader().loadClass(realActivity.getClassName()));
+                    Object.class.getClassLoader().loadClass(mActivityComponent.getClassName()));
         } catch (ClassNotFoundException e) {
             return false;
         }
@@ -843,13 +873,14 @@
             ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
             boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
             ActivityOptions options, ActivityRecord sourceRecord) {
-        service = _service;
+        mAtmService = _service;
+        mRootActivityContainer = _service.mRootActivityContainer;
         appToken = new Token(this, _intent);
         info = aInfo;
         launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
-        userId = UserHandle.getUserId(aInfo.applicationInfo.uid);
+        mUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
         intent = _intent;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
@@ -886,9 +917,9 @@
                 || (aInfo.targetActivity.equals(_intent.getComponent().getClassName())
                 && (aInfo.launchMode == LAUNCH_MULTIPLE
                 || aInfo.launchMode == LAUNCH_SINGLE_TOP))) {
-            realActivity = _intent.getComponent();
+            mActivityComponent = _intent.getComponent();
         } else {
-            realActivity = new ComponentName(aInfo.packageName, aInfo.targetActivity);
+            mActivityComponent = new ComponentName(aInfo.packageName, aInfo.targetActivity);
         }
         taskAffinity = aInfo.taskAffinity;
         stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
@@ -927,7 +958,7 @@
         launchMode = aInfo.launchMode;
 
         Entry ent = AttributeCache.instance().get(packageName,
-                realTheme, com.android.internal.R.styleable.Window, userId);
+                realTheme, com.android.internal.R.styleable.Window, mUserId);
 
         if (ent != null) {
             fullscreen = !ActivityInfo.isTranslucentOrFloating(ent.array);
@@ -991,13 +1022,9 @@
         return hasProcess() && app.hasThread();
     }
 
-    AppWindowContainerController getWindowContainerController() {
-        return mWindowContainerController;
-    }
-
-    void createWindowContainer() {
-        if (mWindowContainerController != null) {
-            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+    void createAppWindowToken() {
+        if (mAppWindowToken != null) {
+            throw new IllegalArgumentException("App Window Token=" + mAppWindowToken
                     + " already created for r=" + this);
         }
 
@@ -1010,12 +1037,31 @@
         // Make sure override configuration is up-to-date before using to create window controller.
         updateOverrideConfiguration();
 
-        mWindowContainerController = new AppWindowContainerController(taskController, appToken,
-                realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation,
-                fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
-                task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
-                appInfo.targetSdkVersion, mRotationAnimationHint,
-                ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+        // TODO: remove after unification
+        mAppWindowToken = mAtmService.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder());
+        if (mAppWindowToken != null) {
+            // TODO: Should this throw an exception instead?
+            Slog.w(TAG, "Attempted to add existing app token: " + appToken);
+        } else {
+            final Task container = taskController.mContainer;
+            if (container == null) {
+                throw new IllegalArgumentException("AppWindowContainerController: invalid "
+                        + " controller=" + taskController);
+            }
+            mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,
+                    task.voiceSession != null, container.getDisplayContent(),
+                    ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this)
+                            * 1000000L, fullscreen,
+                    (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion,
+                    info.screenOrientation, mRotationAnimationHint, info.configChanges,
+                    mLaunchTaskBehind, isAlwaysFocusable());
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) {
+                Slog.v(TAG, "addAppToken: "
+                        + mAppWindowToken + " controller=" + taskController + " at "
+                        + Integer.MAX_VALUE);
+            }
+            container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */);
+        }
 
         task.addActivityToTop(this);
 
@@ -1026,17 +1072,51 @@
         mLastReportedPictureInPictureMode = inPinnedWindowingMode();
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "setAppStartingWindow: token=" + appToken
+                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
+                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
+                    + " allowTaskSnapshot=" + allowTaskSnapshot);
+        }
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken);
+            return false;
+        }
+        return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel,
+                labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch,
+                processRunning, allowTaskSnapshot, activityCreated, fromRecents);
+    }
+
+    // TODO: Remove after unification
+    @VisibleForTesting
+    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
+            boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos,
+            boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
+            int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
+            boolean alwaysFocusable) {
+        return new AppWindowToken(service, token, mActivityComponent, voiceInteraction, dc,
+                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
+                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
+                this);
+    }
+
     void removeWindowContainer() {
-        // Do not try to remove a window container if we have already removed it.
-        if (mWindowContainerController == null) {
+        if (mAtmService.mWindowManager.mRoot == null) return;
+
+        final DisplayContent dc = mAtmService.mWindowManager.mRoot.getDisplayContent(
+                getDisplayId());
+        if (dc == null) {
+            Slog.w(TAG, "removeWindowContainer: Attempted to remove token: "
+                    + appToken + " from non-existing displayId=" + getDisplayId());
             return;
         }
-
         // Resume key dispatching if it is currently paused before we remove the container.
         resumeKeyDispatchingLocked();
-
-        mWindowContainerController.removeContainer(getDisplayId());
-        mWindowContainerController = null;
+        dc.removeAppToken(appToken.asBinder());
     }
 
     /**
@@ -1044,6 +1124,10 @@
      * should ensure that the {@param newTask} is not already the parent of this activity.
      */
     void reparent(TaskRecord newTask, int position, String reason) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken);
+            return;
+        }
         final TaskRecord prevTask = task;
         if (prevTask == newTask) {
             throw new IllegalArgumentException(reason + ": task=" + newTask
@@ -1059,8 +1143,7 @@
                     + " r=" + this + " (" + prevTask.getStackId() + ")");
         }
 
-        // Must reparent first in window manager
-        mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
+        mAppWindowToken.reparent(newTask.getWindowContainerController(), position);
 
         // Reparenting prevents informing the parent stack of activity removal in the case that
         // the new stack has the same parent. we must manually signal here if this is not the case.
@@ -1110,7 +1193,7 @@
      */
     private boolean canLaunchAssistActivity(String packageName) {
         final ComponentName assistComponent =
-                service.mActiveVoiceInteractionServiceComponent;
+                mAtmService.mActiveVoiceInteractionServiceComponent;
         if (assistComponent != null) {
             return assistComponent.getPackageName().equals(packageName);
         }
@@ -1130,8 +1213,8 @@
                 // We only allow home activities to be resizeable if they explicitly requested it.
                 info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             }
-        } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
-                || service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+        } else if (mActivityComponent.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME)
+                || mAtmService.getRecentTasks().isRecentsComponent(mActivityComponent, appInfo.uid)) {
             activityType = ACTIVITY_TYPE_RECENTS;
         } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
                 && canLaunchAssistActivity(launchedFromPackage)) {
@@ -1149,16 +1232,16 @@
     /**
      * @return Stack value from current task, null if there is no task.
      */
-    <T extends ActivityStack> T getStack() {
+    <T extends ActivityStack> T getActivityStack() {
         return task != null ? (T) task.getStack() : null;
     }
 
     int getStackId() {
-        return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+        return getActivityStack() != null ? getActivityStack().mStackId : INVALID_STACK_ID;
     }
 
     ActivityDisplay getDisplay() {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         return stack != null ? stack.getDisplay() : null;
     }
 
@@ -1189,7 +1272,7 @@
     }
 
     boolean isInStackLocked() {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         return stack != null && stack.isInStackLocked(this) != null;
     }
 
@@ -1200,7 +1283,7 @@
     }
 
     boolean isFocusable() {
-        return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
+        return mRootActivityContainer.isFocusable(this, isAlwaysFocusable());
     }
 
     boolean isResizeable() {
@@ -1219,7 +1302,7 @@
      * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
      */
     boolean supportsPictureInPicture() {
-        return service.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
+        return mAtmService.mSupportsPictureInPicture && isActivityTypeStandardOrUndefined()
                 && info.supportsPictureInPicture();
     }
 
@@ -1232,7 +1315,7 @@
         // An activity can not be docked even if it is considered resizeable because it only
         // supports picture-in-picture mode but has a non-resizeable resizeMode
         return super.supportsSplitScreenWindowingMode()
-                && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+                && mAtmService.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
     }
 
     /**
@@ -1240,16 +1323,16 @@
      *         stack.
      */
     boolean supportsFreeform() {
-        return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+        return mAtmService.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
     }
 
     /**
      * @return whether this activity supports non-PiP multi-window.
      */
     private boolean supportsResizeableMultiWindow() {
-        return service.mSupportsMultiWindow && !isActivityTypeHome()
+        return mAtmService.mSupportsMultiWindow && !isActivityTypeHome()
                 && (ActivityInfo.isResizeableMode(info.resizeMode)
-                        || service.mForceResizableActivities);
+                        || mAtmService.mForceResizableActivities);
     }
 
     /**
@@ -1260,7 +1343,7 @@
      *         secondary screen.
      */
     boolean canBeLaunchedOnDisplay(int displayId) {
-        return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
+        return mAtmService.mStackSupervisor.canPlaceEntityOnDisplay(displayId, launchedFromPid,
                 launchedFromUid, info);
     }
 
@@ -1281,13 +1364,13 @@
         }
 
         // Check to see if we are in VR mode, and disallow PiP if so
-        if (service.shouldDisableNonVrUiLocked()) {
+        if (mAtmService.shouldDisableNonVrUiLocked()) {
             return false;
         }
 
-        boolean isKeyguardLocked = service.isKeyguardLocked();
+        boolean isKeyguardLocked = mAtmService.isKeyguardLocked();
         boolean isCurrentAppLocked =
-                service.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+                mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         final ActivityDisplay display = getDisplay();
         boolean hasPinnedStack = display != null && display.hasPinnedStack();
         // Don't return early if !isNotLocked, since we want to throw an exception if the activity
@@ -1328,7 +1411,7 @@
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
     private boolean checkEnterPictureInPictureAppOpsState() {
-        return service.getAppOpsService().checkOperation(
+        return mAtmService.getAppOpsService().checkOperation(
                 OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) == MODE_ALLOWED;
     }
 
@@ -1345,15 +1428,15 @@
             return false;
         }
 
-        final TaskRecord task = getTask();
-        final ActivityStack stack = getStack();
+        final TaskRecord task = getTaskRecord();
+        final ActivityStack stack = getActivityStack();
         if (stack == null) {
             Slog.w(TAG, "moveActivityStackToFront: invalid task or stack: activity="
                     + this + " task=" + task);
             return false;
         }
 
-        if (mStackSupervisor.getTopResumedActivity() == this) {
+        if (mRootActivityContainer.getTopResumedActivity() == this) {
             if (DEBUG_FOCUS) {
                 Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
             }
@@ -1366,9 +1449,9 @@
 
         stack.moveToFront(reason, task);
         // Report top activity change to tracking services and WM
-        if (mStackSupervisor.getTopResumedActivity() == this) {
+        if (mRootActivityContainer.getTopResumedActivity() == this) {
             // TODO(b/111361570): Support multiple focused apps in WM
-            service.setResumedActivityUncheckLocked(this, reason);
+            mAtmService.setResumedActivityUncheckLocked(this, reason);
         }
         return true;
     }
@@ -1378,7 +1461,7 @@
      *         {@link LayoutParams#FLAG_DISMISS_KEYGUARD} set
      */
     boolean hasDismissKeyguardWindows() {
-        return service.mWindowManager.containsDismissKeyguardWindow(appToken);
+        return mAtmService.mWindowManager.containsDismissKeyguardWindow(appToken);
     }
 
     void makeFinishingLocked() {
@@ -1390,14 +1473,14 @@
             clearOptionsLocked();
         }
 
-        if (service != null) {
-            service.getTaskChangeNotificationController().notifyTaskStackChanged();
+        if (mAtmService != null) {
+            mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
         }
     }
 
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
-            uriPermissions = new UriPermissionOwner(service.mUgmInternal, this);
+            uriPermissions = new UriPermissionOwner(mAtmService.mUgmInternal, this);
         }
         return uriPermissions;
     }
@@ -1439,8 +1522,8 @@
     }
 
     final boolean isSleeping() {
-        final ActivityStack stack = getStack();
-        return stack != null ? stack.shouldSleepActivities() : service.isSleepingLocked();
+        final ActivityStack stack = getActivityStack();
+        return stack != null ? stack.shouldSleepActivities() : mAtmService.isSleepingLocked();
     }
 
     /**
@@ -1449,8 +1532,8 @@
      */
     final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
         // The activity now gets access to the data associated with this Intent.
-        service.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
-                intent, getUriPermissionsLocked(), userId);
+        mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
+                intent, getUriPermissionsLocked(), mUserId);
         final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
         boolean unsent = true;
         final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
@@ -1464,7 +1547,7 @@
             try {
                 ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
                 ar.add(rintent);
-                service.getLifecycleManager().scheduleTransaction(
+                mAtmService.getLifecycleManager().scheduleTransaction(
                         app.getThread(), appToken, NewIntentItem.obtain(ar, mState == PAUSED));
                 unsent = false;
             } catch (RemoteException e) {
@@ -1490,7 +1573,7 @@
     void applyOptionsLocked() {
         if (pendingOptions != null
                 && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
-            mWindowContainerController.applyOptionsLocked(pendingOptions, intent);
+            applyOptionsLocked(pendingOptions, intent);
             if (task == null) {
                 clearOptionsLocked(false /* withAbort */);
             } else {
@@ -1500,6 +1583,104 @@
         }
     }
 
+    /**
+     * Apply override app transition base on options & animation type.
+     */
+    void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+        final int animationType = pendingOptions.getAnimationType();
+        final DisplayContent displayContent = mAppWindowToken.getDisplayContent();
+        switch (animationType) {
+            case ANIM_CUSTOM:
+                displayContent.mAppTransition.overridePendingAppTransition(
+                        pendingOptions.getPackageName(),
+                        pendingOptions.getCustomEnterResId(),
+                        pendingOptions.getCustomExitResId(),
+                        pendingOptions.getOnAnimationStartListener());
+                break;
+            case ANIM_CLIP_REVEAL:
+                displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_SCALE_UP:
+                displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getWidth(), pendingOptions.getHeight());
+                if (intent.getSourceBounds() == null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + pendingOptions.getWidth(),
+                            pendingOptions.getStartY() + pendingOptions.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_SCALE_UP:
+            case ANIM_THUMBNAIL_SCALE_DOWN:
+                final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
+                final GraphicBuffer buffer = pendingOptions.getThumbnail();
+                displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
+                        pendingOptions.getStartX(), pendingOptions.getStartY(),
+                        pendingOptions.getOnAnimationStartListener(),
+                        scaleUp);
+                if (intent.getSourceBounds() == null && buffer != null) {
+                    intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                            pendingOptions.getStartY(),
+                            pendingOptions.getStartX() + buffer.getWidth(),
+                            pendingOptions.getStartY() + buffer.getHeight()));
+                }
+                break;
+            case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
+            case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
+                final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
+                final IAppTransitionAnimationSpecsFuture specsFuture =
+                        pendingOptions.getSpecsFuture();
+                if (specsFuture != null) {
+                    // TODO(multidisplay): Shouldn't be really used anymore from next CL.
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
+                            specsFuture, pendingOptions.getOnAnimationStartListener(),
+                            animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
+                } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
+                        && specs != null) {
+                    displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
+                            specs, pendingOptions.getOnAnimationStartListener(),
+                            pendingOptions.getAnimationFinishedListener(), false);
+                } else {
+                    displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
+                            pendingOptions.getThumbnail(),
+                            pendingOptions.getStartX(), pendingOptions.getStartY(),
+                            pendingOptions.getWidth(), pendingOptions.getHeight(),
+                            pendingOptions.getOnAnimationStartListener(),
+                            (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
+                    if (intent.getSourceBounds() == null) {
+                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
+                                pendingOptions.getStartY(),
+                                pendingOptions.getStartX() + pendingOptions.getWidth(),
+                                pendingOptions.getStartY() + pendingOptions.getHeight()));
+                    }
+                }
+                break;
+            case ANIM_OPEN_CROSS_PROFILE_APPS:
+                displayContent.mAppTransition
+                        .overridePendingAppTransitionStartCrossProfileApps();
+                break;
+            case ANIM_REMOTE_ANIMATION:
+                // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
+                displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                        pendingOptions.getRemoteAnimationAdapter());
+                break;
+            case ANIM_NONE:
+                break;
+            default:
+                Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
+                break;
+        }
+    }
+
     ActivityOptions getOptionsForTargetActivityLocked() {
         return pendingOptions != null ? pendingOptions.forTargetActivity() : null;
     }
@@ -1532,8 +1713,11 @@
         if (!keysPaused) {
             keysPaused = true;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.pauseKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1542,8 +1726,11 @@
         if (keysPaused) {
             keysPaused = false;
 
-            if (mWindowContainerController != null) {
-                mWindowContainerController.resumeKeyDispatching();
+            // TODO: remove the check after unification with AppWindowToken. The DC check is not
+            // needed after no mock mAppWindowToken in tests.
+            if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) {
+                mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw(
+                        mAppWindowToken);
             }
         }
     }
@@ -1565,11 +1752,16 @@
     }
 
     void setVisibility(boolean visible) {
-        mWindowContainerController.setVisibility(visible, mDeferHidingClient);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.setVisibility(visible, mDeferHidingClient);
         mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
     }
 
-    // TODO: Look into merging with #setVisibility()
+    // TODO: Look into merging with #commitVisibility()
     void setVisible(boolean newVisible) {
         visible = newVisible;
         mDeferHidingClient = !visible && mDeferHidingClient;
@@ -1589,7 +1781,7 @@
 
         mState = state;
 
-        final TaskRecord parent = getTask();
+        final TaskRecord parent = getTaskRecord();
 
         if (parent != null) {
             parent.onActivityStateChanged(this, state, reason);
@@ -1599,7 +1791,12 @@
         // an indication that the Surface will eventually be destroyed.
         // This however isn't necessarily true if we are going to sleep.
         if (state == STOPPING && !isSleeping()) {
-            mWindowContainerController.notifyAppStopping();
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
+                        + appToken);
+                return;
+            }
+            mAppWindowToken.detachChildren();
         }
     }
 
@@ -1637,7 +1834,12 @@
     }
 
     void notifyAppResumed(boolean wasStopped) {
-        mWindowContainerController.notifyAppResumed(wasStopped);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: "
+                    + appToken);
+            return;
+        }
+        mAppWindowToken.notifyAppResumed(wasStopped);
     }
 
     void notifyUnknownVisibilityLaunched() {
@@ -1645,7 +1847,10 @@
         // No display activities never add a window, so there is no point in waiting them for
         // relayout.
         if (!noDisplay) {
-            mWindowContainerController.notifyUnknownVisibilityLaunched();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController
+                        .notifyLaunched(mAppWindowToken);
+            }
         }
     }
 
@@ -1674,7 +1879,7 @@
         // If this activity is paused, tell it to now show its window.
         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                 "Making visible and scheduling visibility: " + this);
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         try {
             if (stack.mTranslucentActivityWaiting != null) {
                 updateOptionsLocked(returningOptions);
@@ -1702,13 +1907,13 @@
     void makeClientVisible() {
         mClientVisibilityDeferred = false;
         try {
-            service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     WindowVisibilityItem.obtain(true /* showWindow */));
             if (shouldPauseWhenBecomingVisible()) {
                 // An activity must be in the {@link PAUSING} state for the system to validate
                 // the move to {@link PAUSED}.
                 setState(PAUSING, "makeVisibleIfNeeded");
-                service.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
+                mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                         PauseActivityItem.obtain(finishing, false /* userLeaving */,
                                 configChangeFlags, false /* dontReport */));
             }
@@ -1726,7 +1931,7 @@
         // paused state. We also avoid doing this for the activity the stack supervisor
         // considers the resumed activity, as normal means will bring the activity from STOPPED
         // to RESUMED. Adding PAUSING in this scenario will lead to double lifecycles.
-        if (!isState(STOPPED, STOPPING) || getStack().mTranslucentActivityWaiting != null
+        if (!isState(STOPPED, STOPPING) || getActivityStack().mTranslucentActivityWaiting != null
                 || isResumedActivityOnDisplay()) {
             return false;
         }
@@ -1789,8 +1994,8 @@
 
         if (isActivityTypeHome()) {
             WindowProcessController app = task.mActivities.get(0).app;
-            if (hasProcess() && app != service.mHomeProcess) {
-                service.mHomeProcess = app;
+            if (hasProcess() && app != mAtmService.mHomeProcess) {
+                mAtmService.mHomeProcess = app;
             }
         }
 
@@ -1805,7 +2010,7 @@
         mStackSupervisor.reportResumedActivityLocked(this);
 
         resumeKeyDispatchingLocked();
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         mStackSupervisor.mNoAnimActivities.clear();
 
         // Mark the point when the activity is resuming
@@ -1831,7 +2036,7 @@
 
     final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
             CharSequence description) {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (mState != STOPPING) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + this);
             stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this);
@@ -1839,7 +2044,7 @@
         }
         if (newPersistentState != null) {
             persistentState = newPersistentState;
-            service.notifyTaskPersisterLocked(task, false);
+            mAtmService.notifyTaskPersisterLocked(task, false);
         }
         if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle);
 
@@ -1857,16 +2062,18 @@
             stopped = true;
             setState(STOPPED, "activityStoppedLocked");
 
-            mWindowContainerController.notifyAppStopped();
+            if (mAppWindowToken != null) {
+                mAppWindowToken.notifyAppStopped();
+            }
 
             if (finishing) {
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
                     stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 } else {
-                    mStackSupervisor.updatePreviousProcessLocked(this);
+                    mRootActivityContainer.updatePreviousProcess(this);
                 }
             }
         }
@@ -1887,7 +2094,7 @@
             return false;
         }
 
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (stack == null) {
             return false;
         }
@@ -1900,7 +2107,7 @@
 
     void finishLaunchTickingLocked() {
         launchTickTime = 0;
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (stack != null) {
             stack.mHandler.removeMessages(LAUNCH_TICK_MSG);
         }
@@ -1918,14 +2125,33 @@
 
     public void startFreezingScreenLocked(WindowProcessController app, int configChanges) {
         if (mayFreezeScreenLocked(app)) {
-            mWindowContainerController.startFreezingScreen(configChanges);
+            if (mAppWindowToken == null) {
+                Slog.w(TAG_WM,
+                        "Attempted to freeze screen with non-existing app token: " + appToken);
+                return;
+            }
+
+            if (configChanges == 0 && mAppWindowToken.okToDisplay()) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken);
+                return;
+            }
+
+            mAppWindowToken.startFreezingScreen();
         }
     }
 
     public void stopFreezingScreenLocked(boolean force) {
         if (force || frozenBeforeDestroy) {
             frozenBeforeDestroy = false;
-            mWindowContainerController.stopFreezingScreen(force);
+            if (mAppWindowToken == null) {
+                return;
+            }
+            if (DEBUG_ORIENTATION) {
+                Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden="
+                        + mAppWindowToken.isHidden() + " freezing="
+                        + mAppWindowToken.isFreezingScreen());
+            }
+            mAppWindowToken.stopFreezingScreen(true, force);
         }
     }
 
@@ -1937,17 +2163,20 @@
                     info.windowsFullyDrawnDelayMs);
         }
     }
-    @Override
+
+    /**
+     * Called when the starting window for this container is drawn.
+     */
     public void onStartingWindowDrawn(long timestamp) {
-        synchronized (service.mGlobalLock) {
+        synchronized (mAtmService.mGlobalLock) {
             mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
                     getWindowingMode(), timestamp);
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are drawn. */
     public void onWindowsDrawn(boolean drawn, long timestamp) {
-        synchronized (service.mGlobalLock) {
+        synchronized (mAtmService.mGlobalLock) {
             mDrawn = drawn;
             if (!drawn) {
                 return;
@@ -1965,9 +2194,9 @@
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are visible. */
     public void onWindowsVisible() {
-        synchronized (service.mGlobalLock) {
+        synchronized (mAtmService.mGlobalLock) {
             mStackSupervisor.reportActivityVisibleLocked(this);
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
             if (!nowVisible) {
@@ -1994,25 +2223,32 @@
                     mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
                             false /* remove */, true /* processPausingActivities */);
                 }
-                service.scheduleAppGcsLocked();
+                mAtmService.scheduleAppGcsLocked();
             }
         }
     }
 
-    @Override
+    /** Called when the windows associated app window container are no longer visible. */
     public void onWindowsGone() {
-        synchronized (service.mGlobalLock) {
+        synchronized (mAtmService.mGlobalLock) {
             if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this);
             nowVisible = false;
         }
     }
 
-    @Override
+    /**
+     * Called when the key dispatching to a window associated with the app window container
+     * timed-out.
+     *
+     * @param reason The reason for the key dispatching time out.
+     * @param windowPid The pid of the window key dispatching timed out on.
+     * @return True if input dispatching should be aborted.
+     */
     public boolean keyDispatchingTimedOut(String reason, int windowPid) {
         ActivityRecord anrActivity;
         WindowProcessController anrApp;
         boolean windowFromSameProcessAsActivity;
-        synchronized (service.mGlobalLock) {
+        synchronized (mAtmService.mGlobalLock) {
             anrActivity = getWaitingHistoryRecordLocked();
             anrApp = app;
             windowFromSameProcessAsActivity =
@@ -2020,13 +2256,13 @@
         }
 
         if (windowFromSameProcessAsActivity) {
-            return service.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+            return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
                     anrActivity.shortComponentName, anrActivity.appInfo, shortComponentName,
                     app, false, reason);
         } else {
             // In this case another process added windows using this activity token. So, we call the
             // generic service input dispatch timed out method so that the right process is blamed.
-            return service.mAmInternal.inputDispatchingTimedOut(
+            return mAtmService.mAmInternal.inputDispatchingTimedOut(
                     windowPid, false /* aboveSystem */, reason) < 0;
         }
     }
@@ -2036,7 +2272,7 @@
         // another activity to start or has stopped, then the key dispatching
         // timeout should not be caused by this.
         if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) {
-            final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
             // Try to use the one which is closest to top.
             ActivityRecord r = stack.getResumedActivity();
             if (r == null) {
@@ -2053,14 +2289,14 @@
     public boolean okToShowLocked() {
         // We cannot show activities when the device is locked and the application is not
         // encryption aware.
-        if (!StorageManager.isUserKeyUnlocked(userId)
+        if (!StorageManager.isUserKeyUnlocked(mUserId)
                 && !info.applicationInfo.isEncryptionAware()) {
             return false;
         }
 
         return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0
-                || (mStackSupervisor.isCurrentProfileLocked(userId)
-                && service.mAmInternal.isUserRunning(userId, 0 /* flags */));
+                || (mStackSupervisor.isCurrentProfileLocked(mUserId)
+                && mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */));
     }
 
     /**
@@ -2107,13 +2343,13 @@
 
     static ActivityRecord isInStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-        return (r != null) ? r.getStack().isInStackLocked(r) : null;
+        return (r != null) ? r.getActivityStack().isInStackLocked(r) : null;
     }
 
     static ActivityStack getStackLocked(IBinder token) {
         final ActivityRecord r = ActivityRecord.isInStackLocked(token);
         if (r != null) {
-            return r.getStack();
+            return r.getActivityStack();
         }
         return null;
     }
@@ -2123,7 +2359,7 @@
      *         {@link android.view.Display#INVALID_DISPLAY} if not attached.
      */
     int getDisplayId() {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (stack == null) {
             return INVALID_DISPLAY;
         }
@@ -2135,7 +2371,7 @@
             // This would be redundant.
             return false;
         }
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity
                 || !haveState || !stopped) {
             // We're not ready for this kind of thing.
@@ -2161,7 +2397,7 @@
             final File iconFile = new File(TaskPersister.getUserImagesDir(task.userId),
                     iconFilename);
             final String iconFilePath = iconFile.getAbsolutePath();
-            service.getRecentTasks().saveImage(icon, iconFilePath);
+            mAtmService.getRecentTasks().saveImage(icon, iconFilePath);
             _taskDescription.setIconFilename(iconFilePath);
         }
         taskDescription = _taskDescription;
@@ -2183,7 +2419,7 @@
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
             boolean fromRecents) {
-        if (mWindowContainerController == null) {
+        if (mAppWindowToken == null) {
             return;
         }
         if (mTaskOverlay) {
@@ -2197,8 +2433,8 @@
         }
 
         final CompatibilityInfo compatInfo =
-                service.compatibilityInfoForPackageLocked(info.applicationInfo);
-        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
+                mAtmService.compatibilityInfoForPackageLocked(info.applicationInfo);
+        final boolean shown = addStartingWindow(packageName, theme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(),
@@ -2213,34 +2449,62 @@
         if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            mWindowContainerController.removeStartingWindow();
+            mAppWindowToken.removeStartingWindow();
         }
     }
 
     int getRequestedOrientation() {
-        return mWindowContainerController.getOrientation();
+        return getOrientation();
     }
 
     void setRequestedOrientation(int requestedOrientation) {
         final int displayId = getDisplayId();
         final Configuration displayConfig =
-                mStackSupervisor.getDisplayOverrideConfiguration(displayId);
+                mRootActivityContainer.getDisplayOverrideConfiguration(displayId);
 
-        final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,
+        final Configuration config = setOrientation(requestedOrientation,
                 displayId, displayConfig, mayFreezeScreenLocked(app));
         if (config != null) {
             frozenBeforeDestroy = true;
-            if (!service.updateDisplayOverrideConfigurationLocked(config, this,
+            if (!mAtmService.updateDisplayOverrideConfigurationLocked(config, this,
                     false /* deferResume */, displayId)) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
         }
-        service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
                 task.taskId, requestedOrientation);
     }
 
+    Configuration setOrientation(int requestedOrientation, int displayId,
+            Configuration displayConfig, boolean freezeScreenIfNeeded) {
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM,
+                    "Attempted to set orientation of non-existing app token: " + appToken);
+            return null;
+        }
+
+        mAppWindowToken.setOrientation(requestedOrientation);
+
+        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+        return mAtmService.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder,
+                displayId);
+    }
+
+    int getOrientation() {
+        if (mAppWindowToken == null) {
+            return SCREEN_ORIENTATION_UNSPECIFIED;
+        }
+
+        return mAppWindowToken.getOrientationIgnoreVisibility();
+    }
+
     void setDisablePreviewScreenshots(boolean disable) {
-        mWindowContainerController.setDisablePreviewScreenshots(disable);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.setDisablePreviewScreenshots(disable);
     }
 
     /**
@@ -2279,7 +2543,7 @@
 
         // Bounds changed...update configuration to match.
         if (!matchParentBounds()) {
-            task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */,
+            task.computeOverrideConfiguration(mTmpConfig, updatedBounds,
                     false /* overrideWidth */, false /* overrideHeight */);
         }
 
@@ -2288,8 +2552,8 @@
 
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
-        final int orientation = mWindowContainerController != null
-                ? mWindowContainerController.getOrientation() : info.screenOrientation;
+        final int orientation = mAppWindowToken != null
+                ? getOrientation() : info.screenOrientation;
         if (isFixedOrientationPortrait(orientation)
                 && config.orientation != ORIENTATION_PORTRAIT) {
             return false;
@@ -2308,7 +2572,7 @@
     private void computeBounds(Rect outBounds) {
         outBounds.setEmpty();
         final float maxAspectRatio = info.maxAspectRatio;
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0
                 || isInVrUiMode(getConfiguration())) {
             // We don't set override configuration if that activity task isn't fullscreen. I.e. the
@@ -2354,7 +2618,7 @@
         // bounds would end up too small.
         outBounds.set(0, 0, maxActivityWidth + appBounds.left, maxActivityHeight + appBounds.top);
 
-        if (service.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
+        if (mAtmService.mWindowManager.getNavBarPosition(getDisplayId()) == NAV_BAR_LEFT) {
             // Position the activity frame on the opposite side of the nav bar.
             outBounds.left = appBounds.right - maxActivityWidth;
             outBounds.right = appBounds.right;
@@ -2390,7 +2654,7 @@
      */
     boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
             boolean ignoreStopState) {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         if (stack.mConfigWillChange) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Skipping config check (will change): " + this);
@@ -2453,7 +2717,7 @@
         // Update last reported values.
         final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
 
-        setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+        setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
 
         if (mState == INITIALIZING) {
             // No need to relaunch or schedule new config for activity that hasn't been launched
@@ -2507,7 +2771,7 @@
             final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
             if (hasResizeChange) {
                 final boolean isDragResizing =
-                        getTask().getWindowContainerController().isDragResizing();
+                        getTaskRecord().getWindowContainerController().isDragResizing();
                 mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
                         : RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
             } else {
@@ -2640,7 +2904,7 @@
     }
 
     void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
-        if (service.mSuppressResizeConfigChanges && preserveWindow) {
+        if (mAtmService.mSuppressResizeConfigChanges && preserveWindow) {
             configChangeFlags = 0;
             return;
         }
@@ -2656,7 +2920,7 @@
                         + " newIntents=" + pendingNewIntents + " andResume=" + andResume
                         + " preserveWindow=" + preserveWindow);
         EventLog.writeEvent(andResume ? AM_RELAUNCH_RESUME_ACTIVITY
-                        : AM_RELAUNCH_ACTIVITY, userId, System.identityHashCode(this),
+                        : AM_RELAUNCH_ACTIVITY, mUserId, System.identityHashCode(this),
                 task.taskId, shortComponentName);
 
         startFreezingScreenLocked(app, 0);
@@ -2669,7 +2933,7 @@
             mStackSupervisor.activityRelaunchingLocked(this);
             final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
                     pendingNewIntents, configChangeFlags,
-                    new MergedConfiguration(service.getGlobalConfiguration(),
+                    new MergedConfiguration(mAtmService.getGlobalConfiguration(),
                             getMergedOverrideConfiguration()),
                     preserveWindow);
             final ActivityLifecycleItem lifecycleItem;
@@ -2682,7 +2946,7 @@
             final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
             transaction.addCallback(callbackItem);
             transaction.setLifecycleStateRequest(lifecycleItem);
-            service.getLifecycleManager().scheduleTransaction(transaction);
+            mAtmService.getLifecycleManager().scheduleTransaction(transaction);
             // Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
             // request resume if this activity is currently resumed, which implies we aren't
             // sleeping.
@@ -2696,9 +2960,9 @@
             }
             results = null;
             newIntents = null;
-            service.getAppWarningsLocked().onResumeActivity(this);
+            mAtmService.getAppWarningsLocked().onResumeActivity(this);
         } else {
-            final ActivityStack stack = getStack();
+            final ActivityStack stack = getActivityStack();
             if (stack != null) {
                 stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
             }
@@ -2713,7 +2977,7 @@
     private boolean isProcessRunning() {
         WindowProcessController proc = app;
         if (proc == null) {
-            proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
+            proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid);
         }
         return proc != null && proc.hasThread();
     }
@@ -2757,7 +3021,7 @@
             out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
         }
         out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
-        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
 
         if (taskDescription != null) {
             taskDescription.saveToXml(out);
@@ -2867,7 +3131,7 @@
 
     void setShowWhenLocked(boolean showWhenLocked) {
         mShowWhenLocked = showWhenLocked;
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */,
+        mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */,
                 false /* preserveWindows */);
     }
 
@@ -2880,7 +3144,7 @@
      */
     boolean canShowWhenLocked() {
         return !inPinnedWindowingMode() && (mShowWhenLocked
-                || service.mWindowManager.containsShowWhenLockedWindow(appToken));
+                || mAtmService.mWindowManager.containsShowWhenLockedWindow(appToken));
     }
 
     void setTurnScreenOn(boolean turnScreenOn) {
@@ -2895,7 +3159,7 @@
      * @return true if the screen can be turned on, false otherwise.
      */
     boolean canTurnScreenOn() {
-        final ActivityStack stack = getStack();
+        final ActivityStack stack = getActivityStack();
         return mTurnScreenOn && stack != null &&
                 stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
     }
@@ -2905,7 +3169,7 @@
     }
 
     boolean isTopRunningActivity() {
-        return mStackSupervisor.topRunningActivityLocked() == this;
+        return mRootActivityContainer.topRunningActivity() == this;
     }
 
     /**
@@ -2918,7 +3182,12 @@
     }
 
     void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        mWindowContainerController.registerRemoteAnimations(definition);
+        if (mAppWindowToken == null) {
+            Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
+                    + " token: " + appToken);
+            return;
+        }
+        mAppWindowToken.registerRemoteAnimations(definition);
     }
 
     @Override
@@ -2931,7 +3200,7 @@
         sb.append("ActivityRecord{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(" u");
-        sb.append(userId);
+        sb.append(mUserId);
         sb.append(' ');
         sb.append(intent.getComponent().flattenToShortString());
         stringName = sb.toString();
@@ -2941,13 +3210,16 @@
     void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(HASH_CODE, System.identityHashCode(this));
-        proto.write(USER_ID, userId);
+        proto.write(USER_ID, mUserId);
         proto.write(TITLE, intent.getComponent().flattenToShortString());
         proto.end(token);
     }
 
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
+    /**
+     * Write all fields to an {@code ActivityRecordProto}. This assumes the
+     * {@code ActivityRecordProto} is the outer-most proto data.
+     */
+    void writeToProto(ProtoOutputStream proto) {
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
         writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(STATE, mState.toString());
@@ -2957,6 +3229,11 @@
             proto.write(PROC_ID, app.getPid());
         }
         proto.write(TRANSLUCENT, !fullscreen);
+    }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        writeToProto(proto);
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bd3e43c..3ccede0 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -63,7 +63,6 @@
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
-import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -103,6 +102,7 @@
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.RootActivityContainer.FindTaskResult;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -267,7 +267,7 @@
             mStackSupervisor.resizeDockedStackLocked(
                     getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS);
         }
-        mStackSupervisor.updateUIDsPresentOnDisplay();
+        mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     enum ActivityState {
@@ -390,6 +390,7 @@
 
     /** Run all ActivityStacks through this */
     protected final ActivityStackSupervisor mStackSupervisor;
+    protected final RootActivityContainer mRootActivityContainer;
 
     private boolean mTopActivityOccludesKeyguard;
     private ActivityRecord mTopDismissingKeyguardActivity;
@@ -489,6 +490,7 @@
             int windowingMode, int activityType, boolean onTop) {
         mStackSupervisor = supervisor;
         mService = supervisor.mService;
+        mRootActivityContainer = mService.mRootActivityContainer;
         mHandler = new ActivityStackHandler(supervisor.mLooper);
         mWindowManager = mService.mWindowManager;
         mStackId = stackId;
@@ -508,7 +510,7 @@
 
     T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
         return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
+                mRootActivityContainer.mWindowManager);
     }
 
     T getWindowContainerController() {
@@ -532,11 +534,11 @@
             if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                     + reason);
             setResumedActivity(record, reason + " - onActivityStateChanged");
-            if (record == mStackSupervisor.getTopResumedActivity()) {
+            if (record == mRootActivityContainer.getTopResumedActivity()) {
                 // TODO(b/111361570): Support multiple focused apps in WM
                 mService.setResumedActivityUncheckLocked(record, reason);
             }
-            mStackSupervisor.mRecentTasks.add(record.getTask());
+            mStackSupervisor.mRecentTasks.add(record.getTaskRecord());
         }
     }
 
@@ -622,7 +624,7 @@
             display.onStackWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
-            mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
+            mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS,
                     true /* allowResizeInDockedMode */, true /* deferResume */);
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -819,8 +821,8 @@
         }
 
         if (!deferEnsuringVisibility) {
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -854,10 +856,10 @@
     /** Resume next focusable stack after reparenting to another display. */
     void postReparent() {
         adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         // Update visibility of activities before notifying WM. This way it won't try to resize
         // windows that are no longer visible.
-        mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
+        mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
                 !PRESERVE_WINDOWS);
     }
 
@@ -882,7 +884,7 @@
     }
 
     ActivityDisplay getDisplay() {
-        return mStackSupervisor.getActivityDisplay(mDisplayId);
+        return mRootActivityContainer.getActivityDisplay(mDisplayId);
     }
 
     /**
@@ -1034,7 +1036,7 @@
     }
 
     /**
-     * This is a simplified version of topRunningActivityLocked that provides a number of
+     * This is a simplified version of topRunningActivity that provides a number of
      * optional skip-over modes.  It is intended for use with the ActivityController hook only.
      *
      * @param token If non-null, any history records matching this token will be skipped.
@@ -1104,8 +1106,8 @@
         if (r == null) {
             return null;
         }
-        final TaskRecord task = r.getTask();
-        final ActivityStack stack = r.getStack();
+        final TaskRecord task = r.getTaskRecord();
+        final ActivityStack stack = r.getActivityStack();
         if (stack != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
             if (stack != this) Slog.w(TAG,
                     "Illegal state! task does not point to stack it is in.");
@@ -1236,7 +1238,7 @@
 
     boolean isFocusable() {
         final ActivityRecord r = topRunningActivityLocked();
-        return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
+        return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable());
     }
 
     boolean isFocusableAndVisible() {
@@ -1280,7 +1282,7 @@
 
             // Overlays should not be considered as the task's logical top activity.
             final ActivityRecord r = task.getTopActivity(false /* includeOverlays */);
-            if (r == null || r.finishing || r.userId != userId ||
+            if (r == null || r.finishing || r.mUserId != userId ||
                     r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
                 continue;
@@ -1307,7 +1309,7 @@
 
             if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
                     + taskIntent.getComponent().flattenToShortString()
-                    + "/aff=" + r.getTask().rootAffinity + " to new cls="
+                    + "/aff=" + r.getTaskRecord().rootAffinity + " to new cls="
                     + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
             // TODO Refactor to remove duplications. Check if logic can be simplified.
             if (taskIntent != null && taskIntent.getComponent() != null &&
@@ -1366,7 +1368,7 @@
                 if (!r.okToShowLocked()) {
                     continue;
                 }
-                if (!r.finishing && r.userId == userId) {
+                if (!r.finishing && r.mUserId == userId) {
                     if (compareIntentFilters) {
                         if (r.intent.filterEquals(intent)) {
                             return r;
@@ -1398,7 +1400,7 @@
             final TaskRecord task = mTaskHistory.get(i);
 
             if (task.okToShowLocked()) {
-                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
@@ -1450,7 +1452,7 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord ar = activities.get(activityNdx);
 
-                if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
+                if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
                     ar.updateApplicationInfo(aInfo);
                 }
             }
@@ -1536,7 +1538,7 @@
 
     private boolean containsActivityFromStack(List<ActivityRecord> rs) {
         for (ActivityRecord r : rs) {
-            if (r.getStack() == this) {
+            if (r.getActivityStack() == this) {
                 return true;
             }
         }
@@ -1587,7 +1589,7 @@
         if (prev == null) {
             if (resuming == null) {
                 Slog.wtf(TAG, "Trying to pause when nothing is resumed");
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             return false;
         }
@@ -1604,7 +1606,7 @@
         mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
         prev.setState(PAUSING, "startPausingLocked");
-        prev.getTask().touchActiveTime();
+        prev.getTaskRecord().touchActiveTime();
         clearLaunchTime(prev);
 
         mService.updateCpuStats();
@@ -1612,7 +1614,7 @@
         if (prev.attachedToProcess()) {
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
             try {
-                EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
+                EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                         prev.shortComponentName, "userLeaving=" + userLeaving);
                 mService.updateUsageStats(prev, false);
 
@@ -1665,7 +1667,7 @@
             // pause, so just treat it as being paused now.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
             if (resuming == null) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             return false;
         }
@@ -1690,7 +1692,7 @@
                 return;
             } else {
                 EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE,
-                        r.userId, System.identityHashCode(r), r.shortComponentName,
+                        r.mUserId, System.identityHashCode(r), r.shortComponentName,
                         mPausingActivity != null
                             ? mPausingActivity.shortComponentName : "(none)");
                 if (r.isState(PAUSING)) {
@@ -1704,7 +1706,7 @@
                 }
             }
         }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
     }
 
     private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
@@ -1757,9 +1759,9 @@
         }
 
         if (resumeNext) {
-            final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
             if (!topStack.shouldSleepOrShutDownActivities()) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
+                mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null);
             } else {
                 checkReadyForSleep();
                 ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1768,7 +1770,7 @@
                     // something. Also if the top activity on the stack is not the just paused
                     // activity, we need to go ahead and resume it to ensure we complete an
                     // in-flight app switch.
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         }
@@ -1799,7 +1801,7 @@
             mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false;
         }
 
-        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS);
     }
 
     private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) {
@@ -2011,7 +2013,7 @@
     /**
      * Ensure visibility with an option to also update the configuration of visible activities.
      * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
+     * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
      */
     // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
     final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
@@ -2032,7 +2034,7 @@
             boolean aboveTop = top != null;
             final boolean stackShouldBeVisible = shouldBeVisible(starting);
             boolean behindFullscreenActivity = !stackShouldBeVisible;
-            boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this)
+            boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this)
                     && (isInStackLocked(starting) == null);
             final boolean isTopNotPinnedStack =
                     isAttached() && getDisplay().isTopNotPinnedStack(this);
@@ -2443,7 +2445,7 @@
      *
      * NOTE: It is not safe to call this method directly as it can cause an activity in a
      *       non-focused stack to be resumed.
-     *       Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
+     *       Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the
      *       right activity for the current system state.
      */
     @GuardedBy("mService")
@@ -2513,7 +2515,7 @@
             return false;
         }
 
-        mStackSupervisor.cancelInitializingActivities();
+        mRootActivityContainer.cancelInitializingActivities();
 
         // Remember how we'll process this pause/resume situation, and ensure
         // that the state is reset however we wind up proceeding.
@@ -2536,7 +2538,6 @@
             executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
 
@@ -2544,7 +2545,7 @@
         // activity is paused, well that is the state we want.
         if (shouldSleepOrShutDownActivities()
                 && mLastPausedActivity == next
-                && mStackSupervisor.allPausedActivitiesComplete()) {
+                && mRootActivityContainer.allPausedActivitiesComplete()) {
             // If the current top activity may be able to occlude keyguard but the occluded state
             // has not been set, update visibility and check again if we should continue to resume.
             boolean nothingToResume = true;
@@ -2565,7 +2566,6 @@
                 executeAppTransition(options);
                 if (DEBUG_STATES) Slog.d(TAG_STATES,
                         "resumeTopActivityLocked: Going to sleep and all paused");
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return false;
             }
         }
@@ -2573,10 +2573,9 @@
         // Make sure that the user who owns this activity is started.  If not,
         // we will just leave it as is because someone should be bringing
         // another user's activities to the top of the stack.
-        if (!mService.mAmInternal.hasStartedUserState(next.userId)) {
+        if (!mService.mAmInternal.hasStartedUserState(next.mUserId)) {
             Slog.w(TAG, "Skipping resume of top activity " + next
-                    + ": user " + next.userId + " is stopped");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
+                    + ": user " + next.mUserId + " is stopped");
             return false;
         }
 
@@ -2590,10 +2589,9 @@
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
 
         // If we are currently pausing an activity, then don't do anything until that is done.
-        if (!mStackSupervisor.allPausedActivitiesComplete()) {
+        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
             if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                     "resumeTopActivityLocked: Skip resume: some activity pausing.");
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
 
@@ -2640,7 +2638,6 @@
                 next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
                         true /* updateLru */, true /* activityChange */, false /* updateOomAdj */);
             }
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             if (lastResumed != null) {
                 lastResumed.setWillCloseOrEnterPip(true);
             }
@@ -2655,7 +2652,6 @@
             executeAppTransition(options);
             if (DEBUG_STATES) Slog.d(TAG_STATES,
                     "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next);
-            if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return true;
         }
 
@@ -2673,7 +2669,7 @@
 
         if (prev != null && prev != next) {
             if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev)
-                    && next != null && !next.nowVisible) {
+                    && !next.nowVisible) {
                 mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev);
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                         "Resuming top, waiting visible to hide: " + prev);
@@ -2706,7 +2702,7 @@
         // considered stopped.
         try {
             AppGlobals.getPackageManager().setPackageStoppedState(
-                    next.packageName, false, next.userId); /* TODO: Verify if correct userid */
+                    next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
         } catch (RemoteException e1) {
         } catch (IllegalArgumentException e) {
             Slog.w(TAG, "Failed trying to unstop package "
@@ -2727,7 +2723,7 @@
                     dwc.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
                     dwc.prepareAppTransition(
-                            prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_CLOSE
+                            prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_CLOSE
                                     : TRANSIT_TASK_CLOSE, false);
                 }
                 prev.setVisibility(false);
@@ -2739,7 +2735,7 @@
                     dwc.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
                     dwc.prepareAppTransition(
-                            prev.getTask() == next.getTask() ? TRANSIT_ACTIVITY_OPEN
+                            prev.getTaskRecord() == next.getTaskRecord() ? TRANSIT_ACTIVITY_OPEN
                                     : next.mLaunchTaskBehind ? TRANSIT_TASK_OPEN_BEHIND
                                             : TRANSIT_TASK_OPEN, false);
                 }
@@ -2814,7 +2810,7 @@
                 // result of invisible window resize.
                 // TODO: Remove this once visibilities are set correctly immediately when
                 // starting an activity.
-                notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
                         true /* markFrozenIfConfigChanged */, false /* deferResume */);
             }
 
@@ -2836,7 +2832,6 @@
                     next.setVisibility(true);
                 }
                 next.completeResumeLocked();
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
 
@@ -2863,8 +2858,8 @@
                 // Clear app token stopped state in window manager if needed.
                 next.notifyAppResumed(next.stopped);
 
-                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId,
-                        System.identityHashCode(next), next.getTask().taskId,
+                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
+                        System.identityHashCode(next), next.getTaskRecord().taskId,
                         next.shortComponentName);
 
                 next.sleeping = false;
@@ -2899,7 +2894,6 @@
                             false /* taskSwitch */);
                 }
                 mStackSupervisor.startSpecificActivityLocked(next, true, false);
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
 
@@ -2913,7 +2907,6 @@
                 Slog.w(TAG, "Exception thrown during resume of " + next, e);
                 requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                         "resume-exception", true);
-                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                 return true;
             }
         } else {
@@ -2931,7 +2924,6 @@
             mStackSupervisor.startSpecificActivityLocked(next, true, true);
         }
 
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
         return true;
     }
 
@@ -2942,7 +2934,7 @@
             // Try to move focus to the next visible stack with a running activity if this
             // stack is not covering the entire screen or is on a secondary display (with no home
             // stack).
-            return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+            return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev,
                     null /* targetOptions */);
         }
 
@@ -2950,8 +2942,7 @@
         ActivityOptions.abort(options);
         if (DEBUG_STATES) Slog.d(TAG_STATES,
                 "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
-        if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-        return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
+        return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId);
     }
 
     /** Returns the position the input task should be placed in this stack. */
@@ -3017,7 +3008,7 @@
 
     void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
-        TaskRecord rTask = r.getTask();
+        TaskRecord rTask = r.getTaskRecord();
         final int taskId = rTask.taskId;
         // mLaunchTaskBehind tasks get placed at the back of the task stack.
         if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
@@ -3043,7 +3034,7 @@
                     if (!startIt) {
                         if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "
                                 + task, new RuntimeException("here").fillInStackTrace());
-                        r.createWindowContainer();
+                        r.createAppWindowToken();
                         ActivityOptions.abort(options);
                         return;
                     }
@@ -3058,7 +3049,7 @@
 
         // If we are not placing the new activity frontmost, we do not want to deliver the
         // onUserLeaving callback to the actual frontmost activity
-        final TaskRecord activityTask = r.getTask();
+        final TaskRecord activityTask = r.getTaskRecord();
         if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
             mStackSupervisor.mUserLeaving = false;
             if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
@@ -3073,9 +3064,10 @@
         // TODO: Need to investigate if it is okay for the controller to already be created by the
         // time we get to this point. I think it is, but need to double check.
         // Use test in b/34179495 to trace the call path.
-        if (r.getWindowContainerController() == null) {
-            r.createWindowContainer();
+        if (r.mAppWindowToken == null) {
+            r.createAppWindowToken();
         }
+
         task.setFrontOfTask();
 
         if (!isHomeOrRecentsStack() || numActivities() > 0) {
@@ -3130,12 +3122,12 @@
                 // "has the same starting icon" as the next one.  This allows the
                 // window manager to keep the previous window it had previously
                 // created, if it still had one.
-                TaskRecord prevTask = r.getTask();
+                TaskRecord prevTask = r.getTaskRecord();
                 ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();
                 if (prev != null) {
                     // We don't want to reuse the previous starting preview if:
                     // (1) The current activity is in a different task.
-                    if (prev.getTask() != prevTask) {
+                    if (prev.getTaskRecord() != prevTask) {
                         prev = null;
                     }
                     // (2) The current activity is already displayed.
@@ -3168,7 +3160,7 @@
             return false;
         }
         final ActivityStack targetStack = toFrontTask != null
-                ? toFrontTask.getStack() : toFrontActivity.getStack();
+                ? toFrontTask.getStack() : toFrontActivity.getActivityStack();
         if (targetStack != null && targetStack.isActivityTypeAssistant()) {
             // Ensure the task/activity being brought forward is not the assistant
             return false;
@@ -3178,7 +3170,7 @@
 
     private boolean isTaskSwitch(ActivityRecord r,
             ActivityRecord topFocusedActivity) {
-        return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
+        return topFocusedActivity != null && r.getTaskRecord() != topFocusedActivity.getTaskRecord();
     }
 
     /**
@@ -3245,16 +3237,16 @@
                         !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
                                 mTaskHistory.get(0).mActivities.get(0) : null;
                 if (bottom != null && target.taskAffinity != null
-                        && target.taskAffinity.equals(bottom.getTask().affinity)) {
+                        && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
                     // If the activity currently at the bottom has the
                     // same task affinity as the one we are moving,
                     // then merge it into the same task.
-                    targetTask = bottom.getTask();
+                    targetTask = bottom.getTaskRecord();
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to bottom task " + targetTask);
                 } else {
                     targetTask = createTaskRecord(
-                            mStackSupervisor.getNextTaskIdForUserLocked(target.userId),
+                            mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId),
                             target.info, null, null, null, false);
                     targetTask.affinityIntent = target.intent;
                     if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
@@ -3448,7 +3440,7 @@
             ActivityRecord newActivity) {
         final boolean forceReset =
                 (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
-        final TaskRecord task = taskTop.getTask();
+        final TaskRecord task = taskTop.getTaskRecord();
 
         /** False until we evaluate the TaskRecord associated with taskTop. Switches to true
          * for remaining tasks. Used for later tasks to reparent to task. */
@@ -3497,7 +3489,7 @@
 
         if (callingUid > 0) {
             mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
-                    data, r.getUriPermissionsLocked(), r.userId);
+                    data, r.getUriPermissionsLocked(), r.mUserId);
         }
 
         if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
@@ -3536,7 +3528,7 @@
     }
 
     private void adjustFocusedActivityStack(ActivityRecord r, String reason) {
-        if (!mStackSupervisor.isTopDisplayFocusedStack(this) ||
+        if (!mRootActivityContainer.isTopDisplayFocusedStack(this) ||
                 ((mResumedActivity != r) && (mResumedActivity != null))) {
             return;
         }
@@ -3545,7 +3537,7 @@
         final String myReason = reason + " adjustFocus";
 
         if (next == r) {
-            final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
+            final ActivityRecord top = mRootActivityContainer.topRunningActivity();
             if (top != null) {
                 top.moveFocusableActivityToTop(myReason);
             }
@@ -3559,7 +3551,7 @@
 
         // Task is not guaranteed to be non-null. For example, destroying the
         // {@link ActivityRecord} will disassociate the task from the activity.
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
 
         if (task == null) {
             throw new IllegalStateException("activity no longer associated with task:" + r);
@@ -3569,7 +3561,7 @@
         final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason);
         if (nextFocusableStack != null) {
             final ActivityRecord top = nextFocusableStack.topRunningActivityLocked();
-            if (top != null && top == mStackSupervisor.getTopResumedActivity()) {
+            if (top != null && top == mRootActivityContainer.getTopResumedActivity()) {
                 // TODO(b/111361570): Remove this and update focused app per-display in
                 // WindowManager every time an activity becomes resumed in
                 // ActivityTaskManagerService#setResumedActivityUncheckLocked().
@@ -3597,7 +3589,7 @@
      */
     private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
         final ActivityStack stack =
-                mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
+                mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf);
         final String myReason = reason + " adjustFocusToNextFocusableStack";
         if (stack == null) {
             return null;
@@ -3651,7 +3643,7 @@
                     r.setVisible(false);
                 }
                 EventLogTags.writeAmStopActivity(
-                        r.userId, System.identityHashCode(r), r.shortComponentName);
+                        r.mUserId, System.identityHashCode(r), r.shortComponentName);
                 mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
                         StopActivityItem.obtain(r.visible, r.configChangeFlags));
                 if (shouldSleepOrShutDownActivities()) {
@@ -3727,7 +3719,7 @@
         }
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
-        finishedTask = r.getTask();
+        finishedTask = r.getTaskRecord();
         int taskNdx = mTaskHistory.indexOf(finishedTask);
         final TaskRecord task = finishedTask;
         int activityNdx = task.mActivities.indexOf(r);
@@ -3801,7 +3793,7 @@
     }
 
     final boolean finishActivityAffinityLocked(ActivityRecord r) {
-        ArrayList<ActivityRecord> activities = r.getTask().mActivities;
+        ArrayList<ActivityRecord> activities = r.getTaskRecord().mActivities;
         for (int index = activities.indexOf(r); index >= 0; --index) {
             ActivityRecord cur = activities.get(index);
             if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
@@ -3819,15 +3811,15 @@
             if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
                     + " who=" + r.resultWho + " req=" + r.requestCode
                     + " res=" + resultCode + " data=" + resultData);
-            if (resultTo.userId != r.userId) {
+            if (resultTo.mUserId != r.mUserId) {
                 if (resultData != null) {
-                    resultData.prepareToLeaveUser(r.userId);
+                    resultData.prepareToLeaveUser(r.mUserId);
                 }
             }
             if (r.info.applicationInfo.uid > 0) {
                 mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
                         resultTo.packageName, resultData,
-                        resultTo.getUriPermissionsLocked(), resultTo.userId);
+                        resultTo.getUriPermissionsLocked(), resultTo.mUserId);
             }
             resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
             r.resultTo = null;
@@ -3865,9 +3857,9 @@
         mWindowManager.deferSurfaceLayout();
         try {
             r.makeFinishingLocked();
-            final TaskRecord task = r.getTask();
+            final TaskRecord task = r.getTaskRecord();
             EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
-                    r.userId, System.identityHashCode(r),
+                    r.mUserId, System.identityHashCode(r),
                     task.taskId, r.shortComponentName, reason);
             final ArrayList<ActivityRecord> activities = task.mActivities;
             final int index = activities.indexOf(r);
@@ -4000,7 +3992,7 @@
 
         r.setState(FINISHING, "finishCurrentActivityLocked");
         final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
-                && prevState == PAUSED && (r.getStack() != display.getFocusedStack()
+                && prevState == PAUSED && (r.getActivityStack() != display.getFocusedStack()
                         || (next == null && display.topRunningActivity() == null));
 
         if (mode == FINISH_IMMEDIATELY
@@ -4018,11 +4010,11 @@
                 // stack, need to make something visible in its place. Also if the display does not
                 // have running activity, the configuration may need to be updated for restoring
                 // original orientation of the display.
-                mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId,
+                mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
             if (activityRemoved) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
                     "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -4035,7 +4027,7 @@
         if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
         mStackSupervisor.mFinishingActivities.add(r);
         r.resumeKeyDispatchingLocked();
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         return r;
     }
 
@@ -4076,15 +4068,15 @@
     boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
         // Basic case: for simple app-centric recents, we need to recreate
         // the task if the affinity has changed.
-        if (srec == null || srec.getTask().affinity == null ||
-                !srec.getTask().affinity.equals(destAffinity)) {
+        if (srec == null || srec.getTaskRecord().affinity == null ||
+                !srec.getTaskRecord().affinity.equals(destAffinity)) {
             return true;
         }
         // Document-centric case: an app may be split in to multiple documents;
         // they need to re-create their task if this current activity is the root
         // of a document, unless simply finishing it will return them to the the
         // correct app behind.
-        final TaskRecord task = srec.getTask();
+        final TaskRecord task = srec.getTaskRecord();
         if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) {
             // Okay, this activity is at the root of its task.  What to do, what to do...
             if (!inFrontOfStandardStack()) {
@@ -4108,7 +4100,7 @@
 
     final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
             Intent resultData) {
-        final TaskRecord task = srec.getTask();
+        final TaskRecord task = srec.getTaskRecord();
         final ArrayList<ActivityRecord> activities = task.mActivities;
         final int start = activities.indexOf(srec);
         if (!mTaskHistory.contains(task) || (start < 0)) {
@@ -4173,7 +4165,7 @@
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
-                            srec.userId);
+                            srec.mUserId);
                     // TODO(b/64750076): Check if calling pid should really be -1.
                     final int res = mService.getActivityStartController()
                             .obtainStarter(destIntent, "navigateUpTo")
@@ -4290,7 +4282,7 @@
         if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + r);
         r.app = null;
         r.removeWindowContainer();
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         final boolean lastActivity = task != null ? task.removeActivity(r) : false;
         // If we are removing the last activity in the task, not including task overlay activities,
         // then fall through into the block below to remove the entire task itself
@@ -4377,7 +4369,7 @@
             }
         }
         if (activityRemoved) {
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -4454,8 +4446,8 @@
         }
 
         EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
-                r.userId, System.identityHashCode(r),
-                r.getTask().taskId, r.shortComponentName, reason);
+                r.mUserId, System.identityHashCode(r),
+                r.getTaskRecord().taskId, r.shortComponentName, reason);
 
         boolean removedFromHistory = false;
 
@@ -4568,7 +4560,7 @@
             }
         }
 
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4654,8 +4646,8 @@
                         if (!r.finishing) {
                             Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
                             EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
-                                    r.userId, System.identityHashCode(r),
-                                    r.getTask().taskId, r.shortComponentName,
+                                    r.mUserId, System.identityHashCode(r),
+                                    r.getTaskRecord().taskId, r.shortComponentName,
                                     "proc died without state saved");
                             if (r.getState() == RESUMED) {
                                 mService.updateUsageStats(r, false);
@@ -4712,7 +4704,7 @@
                 task.mLastTimeMoved *= -1;
             }
         }
-        mStackSupervisor.invalidateTaskLayers();
+        mRootActivityContainer.invalidateTaskLayers();
     }
 
     final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
@@ -4754,7 +4746,7 @@
             final ActivityRecord top = tr.getTopActivity();
             if (top == null || !top.okToShowLocked()) {
                 if (top != null) {
-                    mStackSupervisor.mRecentTasks.add(top.getTask());
+                    mStackSupervisor.mRecentTasks.add(top.getTaskRecord());
                 }
                 ActivityOptions.abort(options);
                 return;
@@ -4788,7 +4780,7 @@
                 topActivity.supportsEnterPipOnTaskSwitch = true;
             }
 
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
             EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
 
             mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
@@ -4860,7 +4852,7 @@
             return true;
         }
 
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
         return true;
     }
 
@@ -4869,7 +4861,7 @@
         final String strData = data != null ? data.toSafeString() : null;
 
         EventLog.writeEvent(tag,
-                r.userId, System.identityHashCode(r), task.taskId,
+                r.mUserId, System.identityHashCode(r), task.taskId,
                 r.shortComponentName, r.intent.getAction(),
                 r.intent.getType(), strData, r.intent.getFlags());
     }
@@ -4882,15 +4874,15 @@
             return;
         }
 
-        final TaskRecord startTask = start.getTask();
+        final TaskRecord startTask = start.getTaskRecord();
         boolean behindFullscreen = false;
         boolean updatedConfig = false;
 
         for (int taskIndex = mTaskHistory.indexOf(startTask); taskIndex >= 0; --taskIndex) {
             final TaskRecord task = mTaskHistory.get(taskIndex);
             final ArrayList<ActivityRecord> activities = task.mActivities;
-            int activityIndex =
-                    (start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
+            int activityIndex = (start.getTaskRecord() == task)
+                    ? activities.indexOf(start) : activities.size() - 1;
             for (; activityIndex >= 0; --activityIndex) {
                 final ActivityRecord r = activities.get(activityIndex);
                 updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
@@ -4907,7 +4899,7 @@
         if (updatedConfig) {
             // Ensure the resumed state of the focus activity if we updated the configuration of
             // any activity.
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
@@ -4952,9 +4944,12 @@
                 }
             }
 
-            mTmpBounds.put(task.taskId, task.getOverrideBounds());
-            if (tempTaskInsetBounds != null) {
-                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
+            if (task.hasDisplayedBounds()) {
+                mTmpBounds.put(task.taskId, task.getDisplayedBounds());
+                mTmpInsetBounds.put(task.taskId, task.getOverrideBounds());
+            } else {
+                mTmpBounds.put(task.taskId, task.getOverrideBounds());
+                mTmpInsetBounds.put(task.taskId, null);
             }
         }
 
@@ -5054,10 +5049,10 @@
                 ActivityRecord r = mTmpActivities.remove(0);
                 final boolean sameComponent =
                         (r.packageName.equals(packageName) && (filterByClasses == null
-                                || filterByClasses.contains(r.realActivity.getClassName())))
-                        || (packageName == null && r.userId == userId);
-                if ((userId == UserHandle.USER_ALL || r.userId == userId)
-                        && (sameComponent || r.getTask() == lastTask)
+                                || filterByClasses.contains(r.mActivityComponent.getClassName())))
+                        || (packageName == null && r.mUserId == userId);
+                if ((userId == UserHandle.USER_ALL || r.mUserId == userId)
+                        && (sameComponent || r.getTaskRecord() == lastTask)
                         && (r.app == null || evenPersistent || !r.app.isPersistent())) {
                     if (!doit) {
                         if (r.finishing) {
@@ -5068,11 +5063,11 @@
                         return true;
                     }
                     if (r.isActivityTypeHome()) {
-                        if (homeActivity != null && homeActivity.equals(r.realActivity)) {
+                        if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) {
                             Slog.i(TAG, "Skip force-stop again " + r);
                             continue;
                         } else {
-                            homeActivity = r.realActivity;
+                            homeActivity = r.mActivityComponent;
                         }
                     }
                     didSomething = true;
@@ -5083,7 +5078,7 @@
                         }
                         r.app = null;
                     }
-                    lastTask = r.getTask();
+                    lastTask = r.getTaskRecord();
                     finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
                             true);
                 }
@@ -5099,7 +5094,7 @@
      */
     void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
             @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
-        boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this;
+        boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
         boolean topTask = true;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -5164,7 +5159,7 @@
         return removeHistoryRecordsForAppLocked(app);
     }
 
-    void handleAppCrashLocked(WindowProcessController app) {
+    void handleAppCrash(WindowProcessController app) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
@@ -5311,7 +5306,7 @@
             // We only need to adjust focused stack if this stack is in focus and we are not in the
             // process of moving the task to the top of the stack that will be focused.
             if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
-                    && mStackSupervisor.isTopDisplayFocusedStack(this)) {
+                    && mRootActivityContainer.isTopDisplayFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
                 if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
                     getDisplay().moveHomeStackToFront(myReason);
@@ -5417,7 +5412,7 @@
         // The task might have already been running and its visibility needs to be synchronized with
         // the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
@@ -5484,7 +5479,7 @@
         moveToFront(reason);
         // If the original state is resumed, there is no state change to update focused app.
         // So here makes sure the activity focus is set if it is the top.
-        if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) {
+        if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) {
             // TODO(b/111361570): Support multiple focused apps in WM
             mService.setResumedActivityUncheckLocked(r, reason);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 14b2f01c..f3c5630 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -25,28 +25,18 @@
 import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
 import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
 import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
 import static android.app.WaitResult.INVALID_DELAY;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.windowingModeToString;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
-import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -59,26 +49,13 @@
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 
-import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
-import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
-import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
-import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
-import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
-import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
-import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
-import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -86,9 +63,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
@@ -96,6 +71,10 @@
 import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.android.server.wm.RootActivityContainer.TAG_STATES;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
@@ -103,27 +82,15 @@
 import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
-import static java.lang.Integer.MAX_VALUE;
-
 import android.Manifest;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.app.WaitResult;
-import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.LaunchActivityItem;
@@ -139,17 +106,10 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.hardware.display.DisplayManagerInternal;
-import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
-import android.os.FactoryTest;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -163,20 +123,13 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.MediaStore;
-import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.DisplayMetrics;
 import android.util.EventLog;
-import android.util.IntArray;
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.view.Display;
-import android.view.DisplayInfo;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -185,34 +138,28 @@
 import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
-import com.android.server.am.AppTimeTracker;
 import com.android.server.am.EventLogTags;
 import com.android.server.am.UserState;
-import com.android.server.wm.ActivityStack.ActivityState;
-import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
 
 import java.io.FileDescriptor;
 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.Iterator;
 import java.util.List;
-import java.util.Set;
 
-public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
-        RecentTasks.Callbacks, RootWindowContainerListener {
+// TODO: This class has become a dumping ground. Let's
+// - Move things relating to the hierarchy to RootWindowContainer
+// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler
+// - Move interface things to ActivityTaskManagerService.
+// - All other little things to other files.
+public class ActivityStackSupervisor implements RecentTasks.Callbacks {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM;
     private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
     private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
-    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
-    static final String TAG_STATES = TAG + POSTFIX_STATES;
     static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     /** How long we wait until giving up on the last activity telling us it is idle. */
@@ -233,12 +180,6 @@
     static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
     static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
 
-    private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
-
-    // Used to indicate if an object (e.g. stack) that we are trying to get
-    // should be created if it doesn't exist already.
-    static final boolean CREATE_IF_NEEDED = true;
-
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
 
@@ -270,25 +211,6 @@
     private Rect mPendingTempOtherTaskBounds;
     private Rect mPendingTempOtherTaskInsetBounds;
 
-    /**
-     * The modes which affect which tasks are returned when calling
-     * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            MATCH_TASK_IN_STACKS_ONLY,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
-            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-    })
-    public @interface AnyTaskForIdMatchTaskMode {}
-    // Match only tasks in the current stacks
-    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
-    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
-    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
-    // provided stack id
-    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
-
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -315,20 +237,20 @@
     /** The number of distinct task ids that can be assigned to the tasks of a single user */
     private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE;
 
-    ActivityTaskManagerService mService;
+    final ActivityTaskManagerService mService;
+    RootActivityContainer mRootActivityContainer;
 
     /** The historial list of recent tasks including inactive tasks */
     RecentTasks mRecentTasks;
 
     /** Helper class to abstract out logic for fetching the set of currently running tasks */
-    private RunningTasks mRunningTasks;
+    RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
     final Looper mLooper;
 
     /** Short cut */
     WindowManagerService mWindowManager;
-    DisplayManager mDisplayManager;
 
      /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
@@ -341,9 +263,6 @@
      */
     private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20);
 
-    /** The current user */
-    int mCurrentUser;
-
     /** List of activities that are waiting for a new activity to become visible before completing
      * whatever operation they are supposed to do. */
     // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from
@@ -392,9 +311,6 @@
      * is being brought in front of us. */
     boolean mUserLeaving = false;
 
-    /** Set when a power hint has started, but not ended. */
-    private boolean mPowerHintSent;
-
     /**
      * We don't want to allow the device to go to sleep while in the process
      * of launching an activity.  This is primarily to allow alarm intent
@@ -410,29 +326,6 @@
      */
     PowerManager.WakeLock mGoingToSleep;
 
-    /**
-     * A list of tokens that cause the top activity to be put to sleep.
-     * They are used by components that may hide and block interaction with underlying
-     * activities.
-     */
-    final ArrayList<SleepToken> mSleepTokens = new ArrayList<>();
-
-    /** Stack id of the front stack when user switched, indexed by userId. */
-    SparseIntArray mUserStackInFront = new SparseIntArray(2);
-
-    /** Reference to default display so we can quickly look it up. */
-    private ActivityDisplay mDefaultDisplay;
-
-    /**
-     * List of displays which contain activities, sorted by z-order.
-     * The last entry in the list is the topmost.
-     */
-    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
-
-    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
-
-    private DisplayManagerInternal mDisplayManagerInternal;
-
     /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
     boolean inResumeTopActivity;
 
@@ -443,50 +336,8 @@
     private final Rect tempRect = new Rect();
     private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic();
 
-    // The default minimal size that will be used if the activity doesn't specify its minimal size.
-    // It will be calculated when the default display gets added.
-    int mDefaultMinSizeOfResizeableTaskDp = -1;
-
-    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
-    private boolean mTaskLayersChanged = true;
-
     private ActivityMetricsLogger mActivityMetricsLogger;
 
-    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
-
-    @Override
-    protected int getChildCount() {
-        return mActivityDisplays.size();
-    }
-
-    @Override
-    protected ActivityDisplay getChildAt(int index) {
-        return mActivityDisplays.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return null;
-    }
-
-    Configuration getDisplayOverrideConfiguration(int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        return activityDisplay.getOverrideConfiguration();
-    }
-
-    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-
-        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
-    }
-
     /** Check if placing task or activity on specified display is allowed. */
     boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
             ActivityInfo activityInfo) {
@@ -508,44 +359,6 @@
     }
 
     /**
-     * Check if configuration of specified display matches current global config.
-     * Used to check if we can put a non-resizeable activity on a secondary display and it will get
-     * the same config as on the default display.
-     * @param displayId Id of the display to check.
-     * @return {@code true} if configuration matches.
-     */
-    private boolean displayConfigMatchesGlobal(int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            return true;
-        }
-        if (displayId == INVALID_DISPLAY) {
-            return false;
-        }
-        final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (targetDisplay == null) {
-            throw new IllegalArgumentException("No display found with id: " + displayId);
-        }
-        return getConfiguration().equals(targetDisplay.getConfiguration());
-    }
-
-    static class FindTaskResult {
-        ActivityRecord mRecord;
-        boolean mIdealMatch;
-
-        void clear() {
-            mRecord = null;
-            mIdealMatch = false;
-        }
-
-        void setTo(FindTaskResult result) {
-            mRecord = result.mRecord;
-            mIdealMatch = result.mIdealMatch;
-        }
-    }
-
-    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
-
-    /**
      * Used to keep track whether app visibilities got changed since the last pause. Useful to
      * determine whether to invoke the task stack change listener after pausing.
      */
@@ -565,11 +378,6 @@
      */
     private boolean mAllowDockedStackResize = true;
 
-    /**
-     * Is dock currently minimized.
-     */
-    boolean mIsDockMinimized;
-
     private KeyguardController mKeyguardController;
 
     private PowerManager mPowerManager;
@@ -577,8 +385,6 @@
 
     private boolean mInitialized;
 
-    private RootWindowContainerController mWindowContainerController;
-
     /**
      * Description of a request to start a new activity, which has been held
      * due to app switches being disabled.
@@ -617,16 +423,6 @@
         mHandler = new ActivityStackSupervisorHandler(looper);
     }
 
-    @VisibleForTesting
-    void setService(ActivityTaskManagerService service) {
-        mService = service;
-    }
-
-    @VisibleForTesting
-    void setWindowContainerController(RootWindowContainerController controller) {
-        mWindowContainerController = controller;
-    }
-
     public void initialize() {
         if (mInitialized) {
             return;
@@ -634,7 +430,9 @@
 
         mInitialized = true;
         mRunningTasks = createRunningTasks();
-        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext, mHandler.getLooper());
+
+        mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
+                mHandler.getLooper());
         mKeyguardController = new KeyguardController(mService, this);
 
         mPersisterQueue = new PersisterQueue();
@@ -681,321 +479,16 @@
     void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
         getKeyguardController().setWindowManager(wm);
-        setWindowContainerController(new RootWindowContainerController(this));
-
-        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
-        mDisplayManager.registerDisplayListener(this, mHandler);
-        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-
-        final Display[] displays = mDisplayManager.getDisplays();
-        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
-            final Display display = displays[displayNdx];
-            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
-            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
-                mDefaultDisplay = activityDisplay;
-            }
-            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
-        }
-        calculateDefaultMinimalSizeOfResizeableTasks();
-
-        final ActivityDisplay defaultDisplay = getDefaultDisplay();
-
-        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
-    }
-
-    /** Change the z-order of the given display. */
-    private void positionChildAt(ActivityDisplay display, int position) {
-        if (position >= mActivityDisplays.size()) {
-            position = mActivityDisplays.size() - 1;
-        } else if (position < 0) {
-            position = 0;
-        }
-
-        if (mActivityDisplays.isEmpty()) {
-            mActivityDisplays.add(display);
-        } else if (mActivityDisplays.get(position) != display) {
-            mActivityDisplays.remove(display);
-            mActivityDisplays.add(position, display);
-        }
-    }
-
-    @Override
-    public void onChildPositionChanged(DisplayWindowController childController, int position) {
-        // Assume AM lock is held from positionChildAt of controller in each hierarchy.
-        final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
-        if (display != null) {
-            positionChildAt(display, position);
-        }
-    }
-
-    ActivityStack getTopDisplayFocusedStack() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
-            if (focusedStack != null) {
-                return focusedStack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getTopResumedActivity() {
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack == null) {
-            return null;
-        }
-        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
-        if (resumedActivity != null && resumedActivity.app != null) {
-            return resumedActivity;
-        }
-        // The top focused stack might not have a resumed activity yet - look on all displays in
-        // focus order.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
-            if (resumedActivityOnDisplay != null) {
-                return resumedActivityOnDisplay;
-            }
-        }
-        return null;
-    }
-
-    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
-        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
-            return false;
-        }
-
-        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
-    }
-
-    boolean isTopDisplayFocusedStack(ActivityStack stack) {
-        return stack != null && stack == getTopDisplayFocusedStack();
     }
 
     void moveRecentsStackToFront(String reason) {
-        final ActivityStack recentsStack = getDefaultDisplay().getStack(
+        final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
         if (recentsStack != null) {
             recentsStack.moveToFront(reason);
         }
     }
 
-    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
-        if (!mService.isBooting() && !mService.isBooted()) {
-            // Not ready yet!
-            return false;
-        }
-
-        if (displayId == INVALID_DISPLAY) {
-            displayId = DEFAULT_DISPLAY;
-        }
-
-        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
-        final String myReason = reason + " resumeHomeActivity";
-
-        // Only resume home activity if isn't finishing.
-        if (r != null && !r.finishing) {
-            r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
-        }
-        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
-    }
-
-    /**
-     * Check if home activity start should be allowed on a display.
-     * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
-     * @param displayId The id of the target display.
-     * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
-     * @return {@code true} if allow to launch, {@code false} otherwise.
-     */
-    boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
-            boolean allowInstrumenting) {
-        if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
-                && mService.mTopAction == null) {
-            // We are running in factory test mode, but unable to find the factory test app, so
-            // just sit around displaying the error message and don't try to start anything.
-            return false;
-        }
-
-        final WindowProcessController app =
-                mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
-        if (!allowInstrumenting && app != null && app.isInstrumenting()) {
-            // Don't do this if the home app is currently being instrumented.
-            return false;
-        }
-
-        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
-                && displayId == mService.mVr2dDisplayId)) {
-            // No restrictions to default display or vr 2d display.
-            return true;
-        }
-
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
-            // Can't launch home on display that doesn't support system decorations.
-            return false;
-        }
-
-        final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
-                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
-                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
-        if (!supportMultipleInstance) {
-            // Can't launch home on other displays if it requested to be single instance. Also we
-            // don't allow home applications that target before Q to have multiple home activity
-            // instances because they may not be expected to have multiple home scenario and
-            // haven't explicitly request for single instance.
-            return false;
-        }
-
-        return true;
-    }
-
-    TaskRecord anyTaskForIdLocked(int id) {
-        return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
-    }
-
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
-        return anyTaskForIdLocked(id, matchMode, null, !ON_TOP);
-    }
-
-    /**
-     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
-     * @param id Id of the task we would like returned.
-     * @param matchMode The mode to match the given task id in.
-     * @param aOptions The activity options to use for restoration. Can be null.
-     * @param onTop If the stack for the task should be the topmost on the display.
-     */
-    TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode,
-            @Nullable ActivityOptions aOptions, boolean onTop) {
-        // If options are set, ensure that we are attempting to actually restore a task
-        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
-            throw new IllegalArgumentException("Should not specify activity options for non-restore"
-                    + " lookup");
-        }
-
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord task = stack.taskForIdLocked(id);
-                if (task == null) {
-                    continue;
-                }
-                if (aOptions != null) {
-                    // Resolve the stack the task should be placed in now based on options
-                    // and reparent if needed.
-                    final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop);
-                    if (launchStack != null && stack != launchStack) {
-                        final int reparentMode = onTop
-                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
-                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
-                                "anyTaskForIdLocked");
-                    }
-                }
-                return task;
-            }
-        }
-
-        // If we are matching stack tasks only, return now
-        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
-            return null;
-        }
-
-        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
-        // the task from recents
-        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
-        final TaskRecord task = mRecentTasks.getTask(id);
-
-        if (task == null) {
-            if (DEBUG_RECENTS) {
-                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
-            }
-
-            return null;
-        }
-
-        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
-            return task;
-        }
-
-        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
-        if (!restoreRecentTaskLocked(task, aOptions, onTop)) {
-            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
-                    "Couldn't restore task id=" + id + " found in recents");
-            return null;
-        }
-        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
-        return task;
-    }
-
-    ActivityRecord isInAnyStackLocked(IBinder token) {
-        int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.isInStackLocked(token);
-                if (r != null) {
-                    return r;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Detects whether we should show a lock screen in front of this task for a locked user.
-     * <p>
-     * We'll do this if either of the following holds:
-     * <ul>
-     *   <li>The top activity explicitly belongs to {@param userId}.</li>
-     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
-     * </ul>
-     *
-     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
-     */
-    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
-        // To handle the case that work app is in the task but just is not the top one.
-        final ActivityRecord activityRecord = task.getTopActivity();
-        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
-
-        return (activityRecord != null && activityRecord.userId == userId)
-                || (resultTo != null && resultTo.userId == userId);
-    }
-
-    /**
-     * Find all visible task stacks containing {@param userId} and intercept them with an activity
-     * to block out the contents and possibly start a credential-confirming intent.
-     *
-     * @param userId user handle for the locked managed profile.
-     */
-    void lockAllProfileTasks(@UserIdInt int userId) {
-        mWindowManager.deferSurfaceLayout();
-        try {
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    final List<TaskRecord> tasks = stack.getAllTasks();
-                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
-                        final TaskRecord task = tasks.get(taskNdx);
-
-                        // Check the task for a top activity belonging to userId, or returning a
-                        // result to an activity belonging to userId. Example case: a document
-                        // picker for personal files, opened by a work app, should still get locked.
-                        if (taskTopActivityIsUser(task, userId)) {
-                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
-                                    task.taskId, userId);
-                        }
-                    }
-                }
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-    }
-
     void setNextTaskIdForUserLocked(int taskId, int userId) {
         final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
         if (taskId > currentTaskId) {
@@ -1019,7 +512,7 @@
         // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on.
         int candidateTaskId = nextTaskIdForUser(currentTaskId, userId);
         while (mRecentTasks.containsTaskId(candidateTaskId, userId)
-                || anyTaskForIdLocked(
+                || mRootActivityContainer.anyTaskForId(
                         candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
             candidateTaskId = nextTaskIdForUser(candidateTaskId, userId);
             if (candidateTaskId == currentTaskId) {
@@ -1034,142 +527,6 @@
         return candidateTaskId;
     }
 
-    boolean attachApplicationLocked(WindowProcessController app) throws RemoteException {
-        final String processName = app.mName;
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final ActivityStack stack = display.getFocusedStack();
-            if (stack != null) {
-                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
-                final ActivityRecord top = stack.topRunningActivityLocked();
-                final int size = mTmpActivityList.size();
-                for (int i = 0; i < size; i++) {
-                    final ActivityRecord activity = mTmpActivityList.get(i);
-                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
-                            && processName.equals(activity.processName)) {
-                        try {
-                            if (realStartActivityLocked(activity, app,
-                                    top == activity /* andResume */, true /* checkConfig */)) {
-                                didSomething = true;
-                            }
-                        } catch (RemoteException e) {
-                            Slog.w(TAG, "Exception in new application when starting activity "
-                                    + top.intent.getComponent().flattenToShortString(), e);
-                            throw e;
-                        }
-                    }
-                }
-            }
-        }
-        if (!didSomething) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        }
-        return didSomething;
-    }
-
-    boolean allResumedActivitiesIdle() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            // TODO(b/117135575): Check resumed activities on all visible stacks.
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (display.isSleeping()) {
-                // No resumed activities while display is sleeping.
-                continue;
-            }
-
-            // If the focused stack is not null or not empty, there should have some activities
-            // resuming or resumed. Make sure these activities are idle.
-            final ActivityStack stack = display.getFocusedStack();
-            if (stack == null || stack.numActivities() == 0) {
-                continue;
-            }
-            final ActivityRecord resumedActivity = stack.getResumedActivity();
-            if (resumedActivity == null || !resumedActivity.idle) {
-                if (DEBUG_STATES) {
-                    Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
-                            + stack.mStackId + " " + resumedActivity + " not idle");
-                }
-                return false;
-            }
-        }
-        // Send launch end powerhint when idle
-        sendPowerHintForLaunchEndIfNeeded();
-        return true;
-    }
-
-    private boolean allResumedActivitiesVisible() {
-        boolean foundResumed = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.getResumedActivity();
-                if (r != null) {
-                    if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) {
-                        return false;
-                    }
-                    foundResumed = true;
-                }
-            }
-        }
-        return foundResumed;
-    }
-
-    private void executeAppTransitionForAllDisplay() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            display.getWindowContainerController().executeAppTransition();
-        }
-    }
-
-    /**
-     * Pause all activities in either all of the stacks or just the back stacks.
-     * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
-     * @param resuming The resuming activity.
-     * @param dontWait The resuming activity isn't going to wait for all activities to be paused
-     *                 before resuming.
-     * @return true if any activity was paused as a result of this call.
-     */
-    boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
-        boolean someActivityPaused = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            someActivityPaused |= mActivityDisplays.get(displayNdx)
-                    .pauseBackStacks(userLeaving, resuming, dontWait);
-        }
-        return someActivityPaused;
-    }
-
-    boolean allPausedActivitiesComplete() {
-        boolean pausing = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.mPausingActivity;
-                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
-                    if (DEBUG_STATES) {
-                        Slog.d(TAG_STATES,
-                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
-                        pausing = false;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-        }
-        return pausing;
-    }
-
-    void cancelInitializingActivities() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.cancelInitializingActivities();
-            }
-        }
-    }
-
     void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) {
         final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs);
         mWaitingForActivityVisible.add(waitInfo);
@@ -1183,7 +540,7 @@
         mActivitiesWaitingForVisibleActivity.remove(r);
 
         for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
-            if (mWaitingForActivityVisible.get(i).matches(r.realActivity)) {
+            if (mWaitingForActivityVisible.get(i).matches(r.mActivityComponent)) {
                 mWaitingForActivityVisible.remove(i);
             }
         }
@@ -1197,7 +554,7 @@
         boolean changed = false;
         for (int i = mWaitingForActivityVisible.size() - 1; i >= 0; --i) {
             final WaitInfo w = mWaitingForActivityVisible.get(i);
-            if (w.matches(r.realActivity)) {
+            if (w.matches(r.mActivityComponent)) {
                 final WaitResult result = w.getResult();
                 changed = true;
                 result.timeout = false;
@@ -1231,7 +588,7 @@
                 // Unlike START_TASK_TO_FRONT, When an intent is delivered to top, there
                 // will be no followup launch signals. Assign the result and launched component.
                 if (result == START_DELIVERED_TO_TOP) {
-                    w.who = r.realActivity;
+                    w.who = r.mActivityComponent;
                 }
             }
         }
@@ -1260,24 +617,6 @@
         }
     }
 
-    ActivityRecord topRunningActivityLocked() {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
-            if (topActivity != null) {
-                return topActivity;
-            }
-        }
-        return null;
-    }
-
-    @VisibleForTesting
-    void getRunningTasks(int maxNum, List<RunningTaskInfo> list,
-            @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode,
-            int callingUid, boolean allowed) {
-        mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode,
-                mActivityDisplays, callingUid, allowed);
-    }
-
     ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags,
             ProfilerInfo profilerInfo) {
         final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
@@ -1323,31 +662,29 @@
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
             int filterCallingUid) {
-        synchronized (mService.mGlobalLock) {
-            try {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
-                int modifiedFlags = flags
-                        | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
-                if (intent.isWebIntent()
-                            || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
-                    modifiedFlags |= PackageManager.MATCH_INSTANT;
-                }
-
-                // In order to allow cross-profile lookup, we clear the calling identity here.
-                // Note the binder identity won't affect the result, but filterCallingUid will.
-
-                // Cross-user/profile call check are done at the entry points
-                // (e.g. AMS.startActivityAsUser).
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    return mService.getPackageManagerInternalLocked().resolveIntent(
-                            intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            } finally {
-                Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        try {
+            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+            int modifiedFlags = flags
+                    | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
+            if (intent.isWebIntent()
+                        || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
+                modifiedFlags |= PackageManager.MATCH_INSTANT;
             }
+
+            // In order to allow cross-profile lookup, we clear the calling identity here.
+            // Note the binder identity won't affect the result, but filterCallingUid will.
+
+            // Cross-user/profile call check are done at the entry points
+            // (e.g. AMS.startActivityAsUser).
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return mService.getPackageManagerInternalLocked().resolveIntent(
+                        intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
         }
     }
 
@@ -1357,10 +694,10 @@
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
-    private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
+    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
             boolean andResume, boolean checkConfig) throws RemoteException {
 
-        if (!allPausedActivitiesComplete()) {
+        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
             // While there are activities pausing we skipping starting any new activities until
             // pauses are complete. NOTE: that we also do this for activities that are starting in
             // the paused state because they will first be resumed then paused on the client side.
@@ -1370,7 +707,7 @@
             return false;
         }
 
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         final ActivityStack stack = task.getStack();
 
         beginDeferResume();
@@ -1395,11 +732,11 @@
                 // Deferring resume here because we're going to launch new activity shortly.
                 // We don't want to perform a redundant launch of the same record while ensuring
                 // configurations and trying to resume top activity of focused stack.
-                ensureVisibilityAndConfig(r, r.getDisplayId(),
+                mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(),
                         false /* markFrozenIfConfigChanged */, true /* deferResume */);
             }
 
-            if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+            if (r.getActivityStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
                     true /* isTop */)) {
                 // We only set the visibility to true if the activity is allowed to be visible
                 // based on
@@ -1411,7 +748,7 @@
 
             final int applicationInfoUid =
                     (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
-            if ((r.userId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
+            if ((r.mUserId != proc.mUserId) || (r.appInfo.uid != applicationInfoUid)) {
                 Slog.wtf(TAG,
                         "User ID for activity changing for " + r
                                 + " appInfo.uid=" + r.appInfo.uid
@@ -1452,7 +789,7 @@
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                         "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
                                 + " newIntents=" + newIntents + " andResume=" + andResume);
-                EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
+                EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId,
                         System.identityHashCode(r), task.taskId, r.shortComponentName);
                 if (r.isActivityTypeHome()) {
                     // Home process is the root process of the task.
@@ -1565,7 +902,7 @@
         // launching the initial activity (that is, home), so that it can have
         // a chance to initialize itself while in the background, making the
         // switch back to it faster and look better.
-        if (isTopDisplayFocusedStack(stack)) {
+        if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
             mService.getActivityStartController().startSetupActivity();
         }
 
@@ -1578,47 +915,6 @@
         return true;
     }
 
-    /**
-     * Ensure all activities visibility, update orientation and configuration.
-     *
-     * @param starting The currently starting activity or {@code null} if there is none.
-     * @param displayId The id of the display where operation is executed.
-     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
-     *                                  {@code true} if config changed.
-     * @param deferResume Whether to defer resume while updating config.
-     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
-     *         because of configuration update.
-     */
-    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
-            boolean markFrozenIfConfigChanged, boolean deferResume) {
-        // First ensure visibility without updating the config just yet. We need this to know what
-        // activities are affecting configuration now.
-        // Passing null here for 'starting' param value, so that visibility of actual starting
-        // activity will be properly updated.
-        ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
-                false /* preserveWindows */, false /* notifyClients */);
-
-        if (displayId == INVALID_DISPLAY) {
-            // The caller didn't provide a valid display id, skip updating config.
-            return true;
-        }
-
-        // Force-update the orientation from the WindowManager, since we need the true configuration
-        // to send to the client now.
-        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
-                getDisplayOverrideConfiguration(displayId),
-                starting != null && starting.mayFreezeScreenLocked(starting.app)
-                        ? starting.appToken : null,
-                displayId, true /* forceUpdate */);
-        if (starting != null && markFrozenIfConfigChanged && config != null) {
-            starting.frozenBeforeDestroy = true;
-        }
-
-        // Update the configuration of the activities on the display.
-        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
-                displayId);
-    }
-
     private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
         int extrasSize = 0;
         if (intent != null) {
@@ -1674,47 +970,6 @@
         mService.mH.sendMessage(msg);
     }
 
-    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
-        boolean sendHint = forceSend;
-
-        if (!sendHint) {
-            // Send power hint if we don't know what we're launching yet
-            sendHint = targetActivity == null || targetActivity.app == null;
-        }
-
-        if (!sendHint) { // targetActivity != null
-            // Send power hint when the activity's process is different than the current resumed
-            // activity on all displays, or if there are no resumed activities in the system.
-            boolean noResumedActivities = true;
-            boolean allFocusedProcessesDiffer = true;
-            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
-                final WindowProcessController resumedActivityProcess =
-                    resumedActivity == null ? null : resumedActivity.app;
-
-                noResumedActivities &= resumedActivityProcess == null;
-                if (resumedActivityProcess != null) {
-                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
-                }
-            }
-            sendHint = noResumedActivities || allFocusedProcessesDiffer;
-        }
-
-        if (sendHint && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
-            mPowerHintSent = true;
-        }
-    }
-
-    void sendPowerHintForLaunchEndIfNeeded() {
-        // Trigger launch power hint if activity is launched
-        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
-            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
-            mPowerHintSent = false;
-        }
-    }
-
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
             int requestCode, int callingPid, int callingUid, String callingPackage,
             boolean ignoreTargetSecurity, boolean launchingInTask,
@@ -1793,7 +1048,8 @@
             return true;
         }
 
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
+        final ActivityDisplay activityDisplay =
+                mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId);
         if (activityDisplay == null || activityDisplay.isRemoved()) {
             Slog.w(TAG, "Launch on display check: display not found");
             return false;
@@ -1855,21 +1111,6 @@
         return false;
     }
 
-    /** Update lists of UIDs that are present on displays and have access to them. */
-    void updateUIDsPresentOnDisplay() {
-        mDisplayAccessUIDs.clear();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            // Only bother calculating the whitelist for private displays
-            if (activityDisplay.isPrivate()) {
-                mDisplayAccessUIDs.append(
-                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
-            }
-        }
-        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
-        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
-    }
-
     UserInfo getUserInfo(int userId) {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -2021,7 +1262,8 @@
 
             // Check if able to finish booting when device is booting and all resumed activities
             // are idle.
-            if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) {
+            if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle())
+                    || fromTimeout) {
                 booting = checkFinishBootingLocked();
             }
 
@@ -2030,7 +1272,7 @@
             r.mRelaunchReason = RELAUNCH_REASON_NONE;
         }
 
-        if (allResumedActivitiesIdle()) {
+        if (mRootActivityContainer.allResumedActivitiesIdle()) {
             if (r != null) {
                 mService.scheduleAppGcsLocked();
             }
@@ -2043,7 +1285,7 @@
                 }
                 mLaunchingActivity.release();
             }
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         }
 
         // Atomically retrieve all of the other things to do.
@@ -2064,7 +1306,7 @@
         // waiting for the next one to start.
         for (int i = 0; i < NS; i++) {
             r = stops.get(i);
-            final ActivityStack stack = r.getStack();
+            final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
                 if (r.finishing) {
                     stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
@@ -2079,7 +1321,7 @@
         // waiting for the next one to start.
         for (int i = 0; i < NF; i++) {
             r = finishes.get(i);
-            final ActivityStack stack = r.getStack();
+            final ActivityStack stack = r.getActivityStack();
             if (stack != null) {
                 activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
             }
@@ -2099,186 +1341,13 @@
         //mWindowManager.dump();
 
         if (activityRemoved) {
-            resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
 
         return r;
     }
 
-    boolean handleAppDiedLocked(WindowProcessController app) {
-        boolean hasVisibleActivities = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                hasVisibleActivities |= stack.handleAppDiedLocked(app);
-            }
-        }
-        return hasVisibleActivities;
-    }
-
-    void closeSystemDialogsLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.closeSystemDialogsLocked();
-            }
-        }
-    }
-
-    void removeUserLocked(int userId) {
-        mUserStackInFront.delete(userId);
-    }
-
-    /**
-     * Update the last used stack id for non-current user (current user's last
-     * used stack is the focused stack)
-     */
-    void updateUserStackLocked(int userId, ActivityStack stack) {
-        if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
-                    : getDefaultDisplay().getHomeStack().mStackId);
-        }
-    }
-
-    /**
-     * @return true if some activity was finished (or would have finished if doit were true).
-     */
-    boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
-            boolean doit, boolean evenPersistent, int userId) {
-        boolean didSomething = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (stack.finishDisabledPackageActivitiesLocked(
-                        packageName, filterByClasses, doit, evenPersistent, userId)) {
-                    didSomething = true;
-                }
-            }
-        }
-        return didSomething;
-    }
-
-    void updatePreviousProcessLocked(ActivityRecord r) {
-        // Now that this process has stopped, we may want to consider
-        // it to be the previous app to try to keep around in case
-        // the user wants to return to it.
-
-        // First, found out what is currently the foreground app, so that
-        // we don't blow away the previous app if this activity is being
-        // hosted by the process that is actually still the foreground.
-        WindowProcessController fgApp = null;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (isTopDisplayFocusedStack(stack)) {
-                    final ActivityRecord resumedActivity = stack.getResumedActivity();
-                    if (resumedActivity != null) {
-                        fgApp = resumedActivity.app;
-                    } else if (stack.mPausingActivity != null) {
-                        fgApp = stack.mPausingActivity.app;
-                    }
-                    break;
-                }
-            }
-        }
-
-        // Now set this one as the previous process, only if that really
-        // makes sense to.
-        if (r.hasProcess() && fgApp != null && r.app != fgApp
-                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
-                && r.app != mService.mHomeProcess) {
-            mService.mPreviousProcess = r.app;
-            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
-        }
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked() {
-        return resumeFocusedStacksTopActivitiesLocked(null, null, null);
-    }
-
-    boolean resumeFocusedStacksTopActivitiesLocked(
-            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
-
-        if (!readyToResume()) {
-            return false;
-        }
-
-        if (targetStack != null && (targetStack.isTopStackOnDisplay()
-                || getTopDisplayFocusedStack() == targetStack)) {
-            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
-        }
-
-        // Resume all top activities in focused stacks on all displays.
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final ActivityStack focusedStack = display.getFocusedStack();
-            if (focusedStack == null) {
-                continue;
-            }
-            final ActivityRecord r = focusedStack.topRunningActivityLocked();
-            if (r == null || !r.isState(RESUMED)) {
-                focusedStack.resumeTopActivityUncheckedLocked(null, null);
-            } else if (r.isState(RESUMED)) {
-                // Kick off any lingering app transitions form the MoveTaskToFront operation.
-                focusedStack.executeAppTransition(targetOptions);
-            }
-        }
-
-        return false;
-    }
-
-    void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.updateActivityApplicationInfoLocked(aInfo);
-            }
-        }
-    }
-
-    /**
-     * Finish the topmost activities in all stacks that belong to the crashed app.
-     * @param app The app that crashed.
-     * @param reason Reason to perform this action.
-     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
-     */
-    int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) {
-        TaskRecord finishedTask = null;
-        ActivityStack focusedStack = getTopDisplayFocusedStack();
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            // It is possible that request to finish activity might also remove its task and stack,
-            // so we need to be careful with indexes in the loop and check child count every time.
-            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
-                if (stack == focusedStack || finishedTask == null) {
-                    finishedTask = t;
-                }
-            }
-        }
-        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
-    }
-
-    void finishVoiceTask(IVoiceInteractionSession session) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int numStacks = display.getChildCount();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.finishVoiceTask(session);
-            }
-        }
-    }
-
-    /**
-     * This doesn't just find a task, it also moves the task to front.
-     */
+    /** This doesn't just find a task, it also moves the task to front. */
     void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
             boolean forceNonResizeable) {
         ActivityStack currentStack = task.getStack();
@@ -2298,7 +1367,8 @@
             final Rect bounds = options.getLaunchBounds();
             task.updateOverrideConfiguration(bounds);
 
-            ActivityStack stack = getLaunchStack(null, options, task, ON_TOP);
+            ActivityStack stack =
+                    mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
 
             if (stack != currentStack) {
                 moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason);
@@ -2310,7 +1380,7 @@
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
             if (stack.resizeStackWithLaunchBounds()) {
-                resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
+                mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
                         null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
                         true /* allowResizeInDockedMode */, !DEFER_RESUME);
             } else {
@@ -2363,388 +1433,22 @@
         return mLaunchParamsController;
     }
 
-    protected <T extends ActivityStack> T getStack(int stackId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(stackId);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
+    private void deferUpdateRecentsHomeStackBounds() {
+        mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME);
     }
 
-    /** @see ActivityDisplay#getStack(int, int) */
-    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
-            @Nullable TaskRecord task) {
-        // Preference is given to the activity type for the activity then the task since the type
-        // once set shouldn't change.
-        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
-        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
-            activityType = task.getActivityType();
-        }
-        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
-            return activityType;
-        }
-        if (options != null) {
-            activityType = options.getLaunchActivityType();
-        }
-        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
-    }
-
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
-        return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
-    }
-
-    /**
-     * Returns the right stack to use for launching factoring in all the input parameters.
-     *
-     * @param r The activity we are trying to launch. Can be null.
-     * @param options The activity options used to the launch. Can be null.
-     * @param candidateTask The possible task the activity might be launched in. Can be null.
-     * @params launchParams The resolved launch params to use.
-     *
-     * @return The stack to use for the launch or INVALID_STACK_ID.
-     */
-    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
-            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        int taskId = INVALID_TASK_ID;
-        int displayId = INVALID_DISPLAY;
-        //Rect bounds = null;
-
-        // We give preference to the launch preference in activity options.
-        if (options != null) {
-            taskId = options.getLaunchTaskId();
-            displayId = options.getLaunchDisplayId();
-        }
-
-        // First preference for stack goes to the task Id set in the activity options. Use the stack
-        // associated with that if possible.
-        if (taskId != INVALID_TASK_ID) {
-            // Temporarily set the task id to invalid in case in re-entry.
-            options.setLaunchTaskId(INVALID_TASK_ID);
-            final TaskRecord task = anyTaskForIdLocked(taskId,
-                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
-            options.setLaunchTaskId(taskId);
-            if (task != null) {
-                return task.getStack();
-            }
-        }
-
-        final int activityType = resolveActivityType(r, options, candidateTask);
-        T stack;
-
-        // Next preference for stack goes to the display Id set the candidate display.
-        if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
-            displayId = launchParams.mPreferredDisplayId;
-        }
-        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
-            if (r != null) {
-                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
-                        launchParams);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-            final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
-            if (display != null) {
-                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-                if (stack != null) {
-                    return stack;
-                }
-            }
-        }
-
-        // Give preference to the stack and display of the input task and activity if they match the
-        // mode we want to launch into.
-        stack = null;
-        ActivityDisplay display = null;
-        if (candidateTask != null) {
-            stack = candidateTask.getStack();
-        }
-        if (stack == null && r != null) {
-            stack = r.getStack();
-        }
-        if (stack != null) {
-            display = stack.getDisplay();
-            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
-                int windowingMode = launchParams != null ? launchParams.mWindowingMode
-                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-                if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-                    windowingMode = display.resolveWindowingMode(r, options, candidateTask,
-                            activityType);
-                }
-                if (stack.isCompatible(windowingMode, activityType)) {
-                    return stack;
-                }
-                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
-                        && display.getSplitScreenPrimaryStack() == stack
-                        && candidateTask == stack.topTask()) {
-                    // This is a special case when we try to launch an activity that is currently on
-                    // top of split-screen primary stack, but is targeting split-screen secondary.
-                    // In this case we don't want to move it to another stack.
-                    // TODO(b/78788972): Remove after differentiating between preferred and required
-                    // launch options.
-                    return stack;
-                }
-            }
-        }
-
-        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
-            display = getDefaultDisplay();
-        }
-
-        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
-    }
-
-    /** @return true if activity record is null or can be launched on provided display. */
-    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
-        if (r == null) {
-            return true;
-        }
-        return r.canBeLaunchedOnDisplay(displayId);
-    }
-
-    /**
-     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
-     * If there is no such stack, new dynamic stack can be created.
-     * @param displayId Target display.
-     * @param r Activity that should be launched there.
-     * @param candidateTask The possible task the activity might be put in.
-     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
-     */
-    private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException(
-                    "Display with displayId=" + displayId + " not found.");
-        }
-
-        if (!r.canBeLaunchedOnDisplay(displayId)) {
-            return null;
-        }
-
-        // If {@code r} is already in target display and its task is the same as the candidate task,
-        // the intention should be getting a launch stack for the reusable activity, so we can use
-        // the existing stack.
-        if (r.getDisplayId() == displayId && r.getTask() == candidateTask) {
-            return candidateTask.getStack();
-        }
-
-        // Return the topmost valid stack on the display.
-        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = activityDisplay.getChildAt(i);
-            if (isValidLaunchStack(stack, displayId, r)) {
-                return stack;
-            }
-        }
-
-        // If there is no valid stack on the external display - check if new dynamic stack will do.
-        if (displayId != DEFAULT_DISPLAY) {
-            final int windowingMode;
-            if (launchParams != null) {
-                // When launch params is not null, we always defer to its windowing mode. Sometimes
-                // it could be unspecified, which indicates it should inherit windowing mode from
-                // display.
-                windowingMode = launchParams.mWindowingMode;
-            } else {
-                windowingMode = options != null ? options.getLaunchWindowingMode()
-                        : r.getWindowingMode();
-            }
-            final int activityType =
-                    options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
-                            ? options.getLaunchActivityType() : r.getActivityType();
-            return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
-        }
-
-        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
-        return null;
-    }
-
-    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
-            @Nullable ActivityOptions options,
-            @Nullable LaunchParamsController.LaunchParams launchParams) {
-        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
-                launchParams);
-    }
-
-    // TODO: Can probably be consolidated into getLaunchStack()...
-    private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) {
-        switch (stack.getActivityType()) {
-            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
-            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
-            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
-        }
-        // There is a 1-to-1 relationship between stack and task when not in
-        // primary split-windowing mode.
-        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return false;
-        } else {
-            return r.supportsSplitScreenWindowingMode();
-        }
-    }
-
-    /**
-     * Get next focusable stack in the system. This will search through the stack on the same
-     * display as the current focused stack, looking for a focusable and visible stack, different
-     * from the target stack. If no valid candidates will be found, it will then go through all
-     * displays and stacks in last-focused order.
-     *
-     * @param currentFocus The stack that previously had focus.
-     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
-     *                     candidate.
-     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
-     */
-    ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
-            boolean ignoreCurrent) {
-        // First look for next focusable stack on the same display
-        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
-        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
-                currentFocus, ignoreCurrent);
-        if (preferredFocusableStack != null) {
-            return preferredFocusableStack;
-        }
-        if (preferredDisplay.supportsSystemDecorations()) {
-            // Stop looking for focusable stack on other displays because the preferred display
-            // supports system decorations. Home activity would be launched on the same display if
-            // no focusable stack found.
-            return null;
-        }
-
-        // Now look through all displays
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display == preferredDisplay) {
-                // We've already checked this one
-                continue;
-            }
-            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
-                    ignoreCurrent);
-            if (nextFocusableStack != null) {
-                return nextFocusableStack;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Get next valid stack for launching provided activity in the system. This will search across
-     * displays and stacks in last-focused order for a focusable and visible stack, except those
-     * that are on a currently focused display.
-     *
-     * @param r The activity that is being launched.
-     * @param currentFocus The display that previously had focus and thus needs to be ignored when
-     *                     searching for the next candidate.
-     * @return Next valid {@link ActivityStack}, null if not found.
-     */
-    ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            if (display.mDisplayId == currentFocus) {
-                continue;
-            }
-            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
-                    null /* options */, null /* launchParams */);
-            if (stack != null) {
-                return stack;
-            }
-        }
-        return null;
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivity() {
-        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
-    }
-
-    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
-        return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
-    }
-
-    void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
-            boolean deferResume) {
-
-        if (stack.inSplitScreenPrimaryWindowingMode()) {
-            resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
-                    preserveWindows, deferResume);
-            return;
-        }
-
-        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
-        if (!allowResizeInDockedMode
-                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
-            // If the docked stack exists, don't resize non-floating stacks independently of the
-            // size computed from the docked stack size (otherwise they will be out of sync)
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (stack.affectedBySplitScreenResize()) {
-                if (bounds == null && stack.inSplitScreenWindowingMode()) {
-                    // null bounds = fullscreen windowing mode...at least for now.
-                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (splitScreenActive) {
-                    // If we are in split-screen mode and this stack support split-screen, then
-                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
-                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                }
-            }
-            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
-            if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(
-                        stack.topRunningActivityLocked(), preserveWindows);
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    void deferUpdateRecentsHomeStackBounds() {
-        deferUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        deferUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void deferUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.deferUpdateBounds();
-        }
-    }
-
-    void continueUpdateRecentsHomeStackBounds() {
-        continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
-        continueUpdateBounds(ACTIVITY_TYPE_HOME);
-    }
-
-    void continueUpdateBounds(int activityType) {
-        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
-        if (stack != null) {
-            stack.continueUpdateBounds();
-        }
+    private void continueUpdateRecentsHomeStackBounds() {
+        mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS);
+        mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME);
     }
 
     void notifyAppTransitionDone() {
         continueUpdateRecentsHomeStackBounds();
         for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) {
             final int taskId = mResizingTasksDuringAnimation.valueAt(i);
-            final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY);
+            final TaskRecord task =
+                    mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY);
             if (task != null) {
                 task.setTaskDockedResizing(false);
             }
@@ -2763,7 +1467,8 @@
         try {
             final int windowingMode = fromStack.getWindowingMode();
             final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED;
-            final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId);
+            final ActivityDisplay toDisplay =
+                    mRootActivityContainer.getActivityDisplay(toDisplayId);
 
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 // Tell the display we are exiting split-screen mode.
@@ -2820,8 +1525,8 @@
                 }
             }
 
-            ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-            resumeFocusedStacksTopActivitiesLocked();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
         } finally {
             mAllowDockedStackResize = true;
             mWindowManager.continueSurfaceLayout();
@@ -2867,7 +1572,7 @@
                 false /* deferResume */);
     }
 
-    private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
+    void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
             boolean preserveWindows, boolean deferResume) {
 
@@ -2876,7 +1581,8 @@
             return;
         }
 
-        final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        final ActivityStack stack =
+                mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
         if (stack == null) {
             Slog.w(TAG, "resizeDockedStackLocked: docked stack not found");
             return;
@@ -2915,7 +1621,7 @@
                 // static stacks need to be adjusted so they don't overlap with the docked stack.
                 // We get the bounds to use from window manager which has been adjusted for any
                 // screen controls and is also the same for all stacks.
-                final ActivityDisplay display = getDefaultDisplay();
+                final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
                 final Rect otherTaskRect = new Rect();
                 for (int i = display.getChildCount() - 1; i >= 0; --i) {
                     final ActivityStack current = display.getChildAt(i);
@@ -2935,7 +1641,8 @@
                             tempRect /* outStackBounds */,
                             otherTaskRect /* outTempTaskBounds */);
 
-                    resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
+                    mRootActivityContainer.resizeStack(current,
+                            !tempRect.isEmpty() ? tempRect : null,
                             !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
                             tempOtherTaskInsetBounds, preserveWindows,
                             true /* allowResizeInDockedMode */, deferResume);
@@ -2953,7 +1660,8 @@
 
     void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
         // TODO(multi-display): Pinned stack display should be passed in.
-        final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack();
+        final PinnedActivityStack stack =
+                mRootActivityContainer.getDefaultDisplay().getPinnedStack();
         if (stack == null) {
             Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found");
             return;
@@ -3034,22 +1742,6 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
-     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
-     */
-    void removeStacksInWindowingModes(int... windowingModes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
-        }
-    }
-
-    void removeStacksWithActivityTypes(int... activityTypes) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
-        }
-    }
-
-    /**
      * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)}
      */
     boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
@@ -3070,7 +1762,8 @@
      */
     boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
             boolean pauseImmediately, String reason) {
-        final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+        final TaskRecord tr =
+                mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
         if (tr != null) {
             tr.removeTaskActivitiesLocked(pauseImmediately, reason);
             cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
@@ -3159,7 +1852,8 @@
      * @return true if the task has been restored successfully.
      */
     boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) {
-        final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop);
+        final ActivityStack stack =
+                mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop);
         final ActivityStack currentStack = task.getStack();
         if (currentStack != null) {
             // Task has already been restored once. See if we need to do anything more
@@ -3179,7 +1873,7 @@
                 "Added restored task=" + task + " to stack=" + stack);
         final ArrayList<ActivityRecord> activities = task.mActivities;
         for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-            activities.get(activityNdx).createWindowContainer();
+            activities.get(activityNdx).createAppWindowToken();
         }
         return true;
     }
@@ -3201,39 +1895,6 @@
     }
 
     /**
-     * Move stack with all its existing content to specified display.
-     * @param stackId Id of stack to move.
-     * @param displayId Id of display to move stack to.
-     * @param onTop Indicates whether container should be place on top or on bottom.
-     */
-    void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) {
-        final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId);
-        if (activityDisplay == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId="
-                    + displayId);
-        }
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId="
-                    + stackId);
-        }
-
-        final ActivityDisplay currentDisplay = stack.getDisplay();
-        if (currentDisplay == null) {
-            throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack
-                    + " is not attached to any display.");
-        }
-
-        if (currentDisplay.mDisplayId == displayId) {
-            throw new IllegalArgumentException("Trying to move stack=" + stack
-                    + " to its current displayId=" + displayId);
-        }
-
-        stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
-        // TODO(multi-display): resize stacks properly if moved from split-screen.
-    }
-
-    /**
      * 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.
      */
@@ -3285,159 +1946,6 @@
         return stack;
     }
 
-    boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) {
-        final ActivityStack stack = getStack(stackId);
-        if (stack == null) {
-            throw new IllegalArgumentException(
-                    "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
-        }
-
-        final ActivityRecord r = stack.topRunningActivityLocked();
-        if (r == null) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
-                    + " in stack=" + stack);
-            return false;
-        }
-
-        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
-            Slog.w(TAG,
-                    "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
-                            + " r=" + r);
-            return false;
-        }
-
-        moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */,
-                "moveTopActivityToPinnedStack");
-        return true;
-    }
-
-    void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
-            String reason) {
-
-        mWindowManager.deferSurfaceLayout();
-
-        final ActivityDisplay display = r.getStack().getDisplay();
-        PinnedActivityStack stack = display.getPinnedStack();
-
-        // This will clear the pinned stack by moving an existing task to the full screen stack,
-        // ensuring only one task is present.
-        if (stack != null) {
-            moveTasksToFullscreenStackLocked(stack, !ON_TOP);
-        }
-
-        // Need to make sure the pinned stack exist so we can resize it below...
-        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
-
-        // Calculate the target bounds here before the task is reparented back into pinned windowing
-        // mode (which will reset the saved bounds)
-        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
-
-        try {
-            final TaskRecord task = r.getTask();
-            // Resize the pinned stack to match the current size of the task the activity we are
-            // going to be moving is currently contained in. We do this to have the right starting
-            // animation bounds for the pinned stack to the desired bounds the caller wants.
-            resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
-
-            if (task.mActivities.size() == 1) {
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
-                        false /* schedulePictureInPictureModeChange */, reason);
-            } else {
-                // There are multiple activities in the task and moving the top activity should
-                // 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.reparent(newTask, MAX_VALUE, "moveActivityToStack");
-
-                // Defer resume until below, and do not schedule PiP changes until we animate below
-                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
-                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
-            }
-
-            // Reset the state that indicates it can enter PiP while pausing after we've moved it
-            // to the pinned stack
-            r.supportsEnterPipOnTaskSwitch = false;
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-        }
-
-        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
-                true /* fromFullscreen */);
-
-        // Update the visibility of all activities after the they have been reparented to the new
-        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
-        // drawn signal is scheduled after the bounds animation start call on the bounds animator
-        // thread.
-        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        resumeFocusedStacksTopActivitiesLocked();
-
-        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
-    }
-
-    ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
-        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
-        mTmpFindTaskResult.clear();
-
-        // Looking up task on preferred display first
-        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
-        if (preferredDisplay != null) {
-            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (display.mDisplayId == preferredDisplayId) {
-                continue;
-            }
-
-            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
-            }
-        }
-
-        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
-        return mTmpFindTaskResult.mRecord;
-    }
-
-    ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
-            boolean compareIntentFilters) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord ar = stack.findActivityLocked(
-                        intent, info, compareIntentFilters);
-                if (ar != null) {
-                    return ar;
-                }
-            }
-        }
-        return null;
-    }
-
-    boolean hasAwakeDisplay() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            if (!display.shouldSleep()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void goingToSleepLocked() {
         scheduleSleepTimeout();
         if (!mGoingToSleep.isHeld()) {
@@ -3451,24 +1959,19 @@
             }
         }
 
-        applySleepTokensLocked(false /* applyToStacks */);
+        mRootActivityContainer.applySleepTokens(false /* applyToStacks */);
 
         checkReadyForSleepLocked(true /* allowDelay */);
     }
 
-    void prepareForShutdownLocked() {
-        for (int i = 0; i < mActivityDisplays.size(); i++) {
-            createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
-        }
-    }
-
     boolean shutdownLocked(int timeout) {
         goingToSleepLocked();
 
         boolean timedout = false;
         final long endTime = System.currentTimeMillis() + timeout;
         while (true) {
-            if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) {
+            if (!mRootActivityContainer.putStacksToSleep(
+                    true /* allowDelay */, true /* shuttingDown */)) {
                 long timeRemaining = endTime - System.currentTimeMillis();
                 if (timeRemaining > 0) {
                     try {
@@ -3498,54 +2001,9 @@
         }
     }
 
-    void applySleepTokensLocked(boolean applyToStacks) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            // Set the sleeping state of the display.
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final boolean displayShouldSleep = display.shouldSleep();
-            if (displayShouldSleep == display.isSleeping()) {
-                continue;
-            }
-            display.setIsSleeping(displayShouldSleep);
-
-            if (!applyToStacks) {
-                continue;
-            }
-
-            // Set the sleeping state of the stacks on the display.
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (displayShouldSleep) {
-                    stack.goToSleepIfPossible(false /* shuttingDown */);
-                } else {
-                    stack.awakeFromSleepingLocked();
-                    if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
-                            .isKeyguardOrAodShowing(display.mDisplayId)) {
-                        // If the keyguard is unlocked - resume immediately.
-                        // It is possible that the display will not be awake at the time we
-                        // process the keyguard going away, which can happen before the sleep token
-                        // is released. As a result, it is important we resume the activity here.
-                        resumeFocusedStacksTopActivitiesLocked();
-                    }
-                }
-            }
-
-            if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) {
-                continue;
-            }
-            // The display is awake now, so clean up the going to sleep list.
-            for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) {
-                final ActivityRecord r = it.next();
-                if (r.getDisplayId() == display.mDisplayId) {
-                    it.remove();
-                }
-            }
-        }
-    }
-
     void activitySleptLocked(ActivityRecord r) {
         mGoingToSleepActivities.remove(r);
-        final ActivityStack s = r.getStack();
+        final ActivityStack s = r.getActivityStack();
         if (s != null) {
             s.checkReadyForSleep();
         } else {
@@ -3559,12 +2017,13 @@
             return;
         }
 
-        if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) {
+        if (!mRootActivityContainer.putStacksToSleep(
+                allowDelay, false /* shuttingDown */)) {
             return;
         }
 
         // Send launch end powerhint before going sleep
-        sendPowerHintForLaunchEndIfNeeded();
+        mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
 
         removeSleepTimeouts();
 
@@ -3576,55 +2035,27 @@
         }
     }
 
-    // Tries to put all activity stacks to sleep. Returns true if all stacks were
-    // successfully put to sleep.
-    private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
-        boolean allSleep = true;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                if (allowDelay) {
-                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
-                } else {
-                    stack.goToSleep();
-                }
-            }
-        }
-        return allSleep;
-    }
-
     boolean reportResumedActivityLocked(ActivityRecord r) {
         // A resumed activity cannot be stopping. remove from list
         mStoppingActivities.remove(r);
 
-        final ActivityStack stack = r.getStack();
-        if (isTopDisplayFocusedStack(stack)) {
+        final ActivityStack stack = r.getActivityStack();
+        if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
             mService.updateUsageStats(r, true);
         }
         if (stack.getDisplay().allResumedActivitiesComplete()) {
-            ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
             // Make sure activity & window visibility should be identical
             // for all displays in this stage.
-            executeAppTransitionForAllDisplay();
+            mRootActivityContainer.executeAppTransitionForAllDisplay();
             return true;
         }
         return false;
     }
 
-    void handleAppCrashLocked(WindowProcessController app) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.handleAppCrashLocked(app);
-            }
-        }
-    }
-
     // Called when WindowManager has finished animating the launchingBehind activity to the back.
     private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         final ActivityStack stack = task.getStack();
 
         r.mLaunchTaskBehind = false;
@@ -3636,7 +2067,7 @@
         // task has been shown briefly
         final ActivityRecord top = stack.getTopActivity();
         if (top != null) {
-            top.getTask().touchActiveTime();
+            top.getTaskRecord().touchActiveTime();
         }
     }
 
@@ -3644,157 +2075,9 @@
         mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget();
     }
 
-    /**
-     * Make sure that all activities that need to be visible in the system actually are and update
-     * their configuration.
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows) {
-        ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                true /* notifyClients */);
-    }
-
-    /**
-     * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean)
-     */
-    void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-            boolean preserveWindows, boolean notifyClients) {
-        getKeyguardController().beginActivityVisibilityUpdate();
-        try {
-            // First the front stacks. In case any are not fullscreen and are in front of home.
-            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
-                            notifyClients);
-                }
-            }
-        } finally {
-            getKeyguardController().endActivityVisibilityUpdate();
-        }
-    }
-
-    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.addStartingWindowsForVisibleActivities(taskSwitch);
-            }
-        }
-    }
-
-    void invalidateTaskLayers() {
-        mTaskLayersChanged = true;
-    }
-
-    void rankTaskLayersIfNeeded() {
-        if (!mTaskLayersChanged) {
-            return;
-        }
-        mTaskLayersChanged = false;
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            int baseLayer = 0;
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                baseLayer += stack.rankTaskLayers(baseLayer);
-            }
-        }
-    }
-
-    void clearOtherAppTimeTrackers(AppTimeTracker except) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.clearOtherAppTimeTrackers(except);
-            }
-        }
-    }
-
-    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.scheduleDestroyActivities(app, reason);
-            }
-        }
-    }
-
-    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
-        // Tasks is non-null only if two or more tasks are found.
-        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
-        if (tasks == null) {
-            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
-            return;
-        }
-        // If we have activities in multiple tasks that are in a position to be destroyed,
-        // let's iterate through the tasks and release the oldest one.
-        final int numDisplays = mActivityDisplays.size();
-        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int stackCount = display.getChildCount();
-            // Step through all stacks starting from behind, to hit the oldest things first.
-            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                // Try to release activities in this stack; if we manage to, we are done.
-                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
-                    return;
-                }
-            }
-        }
-    }
-
-    boolean switchUserLocked(int userId, UserState uss) {
-        final int focusStackId = getTopDisplayFocusedStack().getStackId();
-        // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
-        if (dockedStack != null) {
-            moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay());
-        }
-        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
-        // also cause all tasks to be moved to the fullscreen stack at a position that is
-        // appropriate.
-        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
-        mUserStackInFront.put(mCurrentUser, focusStackId);
-        final int restoreStackId =
-                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
-        mCurrentUser = userId;
-
-        mStartingUsers.add(uss);
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                stack.switchUserLocked(userId);
-                TaskRecord task = stack.topTask();
-                if (task != null) {
-                    stack.positionChildWindowContainerAtTop(task);
-                }
-            }
-        }
-
-        ActivityStack stack = getStack(restoreStackId);
-        if (stack == null) {
-            stack = getDefaultDisplay().getHomeStack();
-        }
-        final boolean homeInFront = stack.isActivityTypeHome();
-        if (stack.isOnHomeDisplay()) {
-            stack.moveToFront("switchUserOnHomeDisplay");
-        } else {
-            // Stack was moved to another display while user was swapped out.
-            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
-        }
-        return homeInFront;
-    }
-
     /** Checks whether the userid is a profile of the current user. */
     boolean isCurrentProfileLocked(int userId) {
-        if (userId == mCurrentUser) return true;
+        if (userId == mRootActivityContainer.mCurrentUser) return true;
         return mService.mAmInternal.isCurrentProfile(userId);
     }
 
@@ -3819,7 +2102,7 @@
             boolean remove, boolean processPausingActivities) {
         ArrayList<ActivityRecord> stops = null;
 
-        final boolean nowVisible = allResumedActivitiesVisible();
+        final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible();
         for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord s = mStoppingActivities.get(activityNdx);
             boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s);
@@ -3839,7 +2122,7 @@
                 }
             }
             if (remove) {
-                final ActivityStack stack = s.getStack();
+                final ActivityStack stack = s.getActivityStack();
                 final boolean shouldSleepOrShutDown = stack != null
                         ? stack.shouldSleepOrShutDownActivities()
                         : mService.isSleepingOrShuttingDownLocked();
@@ -3869,134 +2152,26 @@
         return stops;
     }
 
-    void validateTopActivitiesLocked() {
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                final ActivityRecord r = stack.topRunningActivityLocked();
-                final ActivityState state = r == null ? DESTROYED : r.getState();
-                if (isTopDisplayFocusedStack(stack)) {
-                    if (r == null) Slog.e(TAG,
-                            "validateTop...: null top activity, stack=" + stack);
-                    else {
-                        final ActivityRecord pausing = stack.mPausingActivity;
-                        if (pausing != null && pausing == r) Slog.e(TAG,
-                                "validateTop...: top stack has pausing activity r=" + r
-                                + " state=" + state);
-                        if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
-                                "validateTop...: activity in front not resumed r=" + r
-                                + " state=" + state);
-                    }
-                } else {
-                    final ActivityRecord resumed = stack.getResumedActivity();
-                    if (resumed != null && resumed == r) Slog.e(TAG,
-                            "validateTop...: back stack has resumed activity r=" + r
-                            + " state=" + state);
-                    if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
-                            "validateTop...: activity in back resumed r=" + r + " state=" + state);
-                }
-            }
-        }
-    }
-
-    public void dumpDisplays(PrintWriter pw) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            pw.print("[id:" + display.mDisplayId + " stacks:");
-            display.dumpStacks(pw);
-            pw.print("]");
-        }
-    }
-
     public void dump(PrintWriter pw, String prefix) {
         pw.println();
         pw.println("ActivityStackSupervisor state:");
-        pw.print(prefix);
-        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        mRootActivityContainer.dump(pw, prefix);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
-        pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            display.dump(pw, prefix);
-        }
+        pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront);
         if (!mWaitingForActivityVisible.isEmpty()) {
-            pw.print(prefix); pw.println("mWaitingForActivityVisible=");
+            pw.println(prefix + "mWaitingForActivityVisible=");
             for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) {
-                pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
+                pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix);
             }
         }
         pw.print(prefix); pw.print("isHomeRecentsComponent=");
-        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser));
 
         getKeyguardController().dump(pw, prefix);
         mService.getLockTaskController().dump(pw, prefix);
     }
 
-    public void writeToProto(ProtoOutputStream proto, long fieldId) {
-        final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            activityDisplay.writeToProto(proto, DISPLAYS);
-        }
-        getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
-        // TODO(b/111541062): Update tests to look for resumed activities on all displays
-        final ActivityStack focusedStack = getTopDisplayFocusedStack();
-        if (focusedStack != null) {
-            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
-            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
-            if (focusedActivity != null) {
-                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
-            }
-        } else {
-            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
-        }
-        proto.write(IS_HOME_RECENTS_COMPONENT,
-                mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
-        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
-        proto.end(token);
-    }
-
-    /**
-     * Dump all connected displays' configurations.
-     * @param prefix Prefix to apply to each line of the dump.
-     */
-    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.println("Display override configurations:");
-        final int displayCount = mActivityDisplays.size();
-        for (int i = 0; i < displayCount; i++) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
-                    pw.println(activityDisplay.getOverrideConfiguration());
-        }
-    }
-
-    /**
-     * 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 dumpVisibleStacksOnly,
-            boolean dumpFocusedStackOnly) {
-        if (dumpFocusedStackOnly) {
-            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
-        } else {
-            ArrayList<ActivityRecord> activities = new ArrayList<>();
-            int numDisplays = mActivityDisplays.size();
-            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
-                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
-                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
-                        activities.addAll(stack.getDumpActivitiesLocked(name));
-                    }
-                }
-            }
-            return activities;
-        }
-    }
-
     static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage,
             boolean needSep, String prefix) {
         if (activity != null) {
@@ -4012,73 +2187,6 @@
         return false;
     }
 
-    boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
-            boolean dumpClient, String dumpPackage) {
-        boolean printed = false;
-        boolean needSep = false;
-        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
-            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
-                    pw.println(" (activities from top to bottom):");
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                pw.println();
-                pw.println("  Stack #" + stack.mStackId
-                        + ": type=" + activityTypeToString(stack.getActivityType())
-                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
-                pw.println("  isSleeping=" + stack.shouldSleepActivities());
-                pw.println("  mBounds=" + stack.getOverrideBounds());
-
-                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
-                        needSep);
-
-                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
-                        !dumpAll, false, dumpPackage, true,
-                        "    Running activities (most recent first):", null);
-
-                needSep = printed;
-                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
-                        "    mPausingActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
-                        "    mResumedActivity: ");
-                if (pr) {
-                    printed = true;
-                    needSep = false;
-                }
-                if (dumpAll) {
-                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
-                            "    mLastPausedActivity: ");
-                    if (pr) {
-                        printed = true;
-                        needSep = true;
-                    }
-                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
-                            needSep, "    mLastNoHistoryActivity: ");
-                }
-                needSep = printed;
-            }
-            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
-                    " ResumedActivity:");
-        }
-
-        printed |= dumpHistoryList(fd, pw, mFinishingActivities, "  ", "Fin", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to finish:", null);
-        printed |= dumpHistoryList(fd, pw, mStoppingActivities, "  ", "Stop", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to stop:", null);
-        printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, "  ", "Wait",
-                false, !dumpAll, false, dumpPackage, true,
-                "  Activities waiting for another to become visible:", null);
-        printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, "  ", "Sleep", false, !dumpAll,
-                false, dumpPackage, true, "  Activities waiting to sleep:", null);
-
-        return printed;
-    }
-
     static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list,
             String prefix, String label, boolean complete, boolean brief, boolean client,
             String dumpPackage, boolean needNL, String header, TaskRecord lastTask) {
@@ -4104,8 +2212,8 @@
                 pw.println(header);
                 header = null;
             }
-            if (lastTask != r.getTask()) {
-                lastTask = r.getTask();
+            if (lastTask != r.getTaskRecord()) {
+                lastTask = r.getTaskRecord();
                 pw.print(prefix);
                 pw.print(full ? "* " : "  ");
                 pw.println(lastTask);
@@ -4188,294 +2296,6 @@
         mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT);
     }
 
-    @Override
-    public void onDisplayAdded(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
-        synchronized (mService.mGlobalLock) {
-            getActivityDisplayOrCreateLocked(displayId);
-            // Do not start home before booting, or it may accidentally finish booting before it
-            // starts. Instead, we expect home activities to be launched when the system is ready
-            // (ActivityManagerService#systemReady).
-            if (mService.isBooted() || mService.isBooting()) {
-                startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
-            }
-        }
-    }
-
-    @Override
-    public void onDisplayRemoved(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
-        if (displayId == DEFAULT_DISPLAY) {
-            throw new IllegalArgumentException("Can't remove the primary display.");
-        }
-
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay == null) {
-                return;
-            }
-
-            activityDisplay.remove();
-        }
-    }
-
-    @Override
-    public void onDisplayChanged(int displayId) {
-        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
-        synchronized (mService.mGlobalLock) {
-            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-            if (activityDisplay != null) {
-                activityDisplay.onDisplayChanged();
-            }
-        }
-    }
-
-    /** Check if display with specified id is added to the list. */
-    boolean isDisplayAdded(int displayId) {
-        return getActivityDisplayOrCreateLocked(displayId) != null;
-    }
-
-    // TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
-    ActivityDisplay getActivityDisplay(int displayId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
-            if (activityDisplay.mDisplayId == displayId) {
-                return activityDisplay;
-            }
-        }
-        return null;
-    }
-
-    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
-    ActivityDisplay getDefaultDisplay() {
-        return mDefaultDisplay;
-    }
-
-    /**
-     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
-     * corresponding record in display manager.
-     */
-    // TODO: Look into consolidating with getActivityDisplay()
-    ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
-        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
-        if (activityDisplay != null) {
-            return activityDisplay;
-        }
-        if (mDisplayManager == null) {
-            // The system isn't fully initialized yet.
-            return null;
-        }
-        final Display display = mDisplayManager.getDisplay(displayId);
-        if (display == null) {
-            // The display is not registered in DisplayManager.
-            return null;
-        }
-        // The display hasn't been added to ActivityManager yet, create a new record now.
-        activityDisplay = new ActivityDisplay(this, display);
-        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
-        return activityDisplay;
-    }
-
-    /**
-     * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
-     * defined in {@link DisplayInfo#uniqueId}.
-     *
-     * @param uniqueId the unique ID of the display
-     * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
-     */
-    ActivityDisplay getActivityDisplay(String uniqueId) {
-        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            final boolean isValid = display.mDisplay.isValid();
-            if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
-                return display;
-            }
-        }
-
-        return null;
-    }
-
-    boolean startHomeOnAllDisplays(int userId, String reason) {
-        boolean homeStarted = false;
-        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
-            final int displayId = mActivityDisplays.get(i).mDisplayId;
-            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
-        }
-        return homeStarted;
-    }
-
-    /**
-     * This starts home activity on displays that can have system decorations and only if the
-     * home activity can have multiple instances.
-     */
-    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
-        final Intent homeIntent = mService.getHomeIntent();
-        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
-        if (aInfo == null) {
-            return false;
-        }
-
-        if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) {
-            return false;
-        }
-
-        // Update the reason for ANR debugging to verify if the user activity is the one that
-        // actually launched.
-        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
-                aInfo.applicationInfo.uid);
-        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
-                displayId);
-        return true;
-    }
-
-    /**
-     * This resolves the home activity info and updates the home component of the given intent.
-     * @return the home activity info if any.
-     */
-    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
-        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
-        final ComponentName comp = homeIntent.getComponent();
-        ActivityInfo aInfo = null;
-        try {
-            if (comp != null) {
-                // Factory test.
-                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
-            } else {
-                final String resolvedType =
-                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
-                final ResolveInfo info = AppGlobals.getPackageManager()
-                        .resolveIntent(homeIntent, resolvedType, flags, userId);
-                if (info != null) {
-                    aInfo = info.activityInfo;
-                }
-            }
-        } catch (RemoteException e) {
-            // ignore
-        }
-
-        if (aInfo == null) {
-            Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
-            return null;
-        }
-
-        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
-        aInfo = new ActivityInfo(aInfo);
-        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
-        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
-        return aInfo;
-    }
-
-    @VisibleForTesting
-    void addChild(ActivityDisplay activityDisplay, int position) {
-        positionChildAt(activityDisplay, position);
-        mWindowContainerController.positionChildAt(
-                activityDisplay.getWindowContainerController(), position);
-    }
-
-    void removeChild(ActivityDisplay activityDisplay) {
-        // The caller must tell the controller of {@link ActivityDisplay} to release its container
-        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
-        mActivityDisplays.remove(activityDisplay);
-    }
-
-    private void calculateDefaultMinimalSizeOfResizeableTasks() {
-        final Resources res = mService.mContext.getResources();
-        final float minimalSize = res.getDimension(
-                com.android.internal.R.dimen.default_minimal_size_resizable_task);
-        final DisplayMetrics dm = res.getDisplayMetrics();
-
-        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
-    }
-
-    SleepToken createSleepTokenLocked(String tag, int displayId) {
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        if (display == null) {
-            throw new IllegalArgumentException("Invalid display: " + displayId);
-        }
-
-        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
-        mSleepTokens.add(token);
-        display.mAllSleepTokens.add(token);
-        return token;
-    }
-
-    private void removeSleepTokenLocked(SleepTokenImpl token) {
-        mSleepTokens.remove(token);
-
-        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
-        if (display != null) {
-            display.mAllSleepTokens.remove(token);
-            if (display.mAllSleepTokens.isEmpty()) {
-                mService.updateSleepIfNeededLocked();
-            }
-        }
-    }
-
-    private StackInfo getStackInfo(ActivityStack stack) {
-        final int displayId = stack.mDisplayId;
-        final ActivityDisplay display = getActivityDisplay(displayId);
-        StackInfo info = new StackInfo();
-        stack.getWindowContainerBounds(info.bounds);
-        info.displayId = displayId;
-        info.stackId = stack.mStackId;
-        info.userId = stack.mCurrentUser;
-        info.visible = stack.shouldBeVisible(null);
-        // A stack might be not attached to a display.
-        info.position = display != null ? display.getIndexOf(stack) : 0;
-        info.configuration.setTo(stack.getConfiguration());
-
-        ArrayList<TaskRecord> tasks = stack.getAllTasks();
-        final int numTasks = tasks.size();
-        int[] taskIds = new int[numTasks];
-        String[] taskNames = new String[numTasks];
-        Rect[] taskBounds = new Rect[numTasks];
-        int[] taskUserIds = new int[numTasks];
-        for (int i = 0; i < numTasks; ++i) {
-            final TaskRecord task = tasks.get(i);
-            taskIds[i] = task.taskId;
-            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
-                    : task.realActivity != null ? task.realActivity.flattenToString()
-                    : task.getTopActivity() != null ? task.getTopActivity().packageName
-                    : "unknown";
-            taskBounds[i] = new Rect();
-            task.getWindowContainerBounds(taskBounds[i]);
-            taskUserIds[i] = task.userId;
-        }
-        info.taskIds = taskIds;
-        info.taskNames = taskNames;
-        info.taskBounds = taskBounds;
-        info.taskUserIds = taskUserIds;
-
-        final ActivityRecord top = stack.topRunningActivityLocked();
-        info.topActivity = top != null ? top.intent.getComponent() : null;
-        return info;
-    }
-
-    StackInfo getStackInfo(int stackId) {
-        ActivityStack stack = getStack(stackId);
-        if (stack != null) {
-            return getStackInfo(stack);
-        }
-        return null;
-    }
-
-    StackInfo getStackInfo(int windowingMode, int activityType) {
-        final ActivityStack stack = getStack(windowingMode, activityType);
-        return (stack != null) ? getStackInfo(stack) : null;
-    }
-
-    ArrayList<StackInfo> getAllStackInfosLocked() {
-        ArrayList<StackInfo> list = new ArrayList<>();
-        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
-            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-                list.add(getStackInfo(stack));
-            }
-        }
-        return list;
-    }
-
     void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode,
             int preferredDisplayId, ActivityStack actualStack) {
         handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId,
@@ -4550,7 +2370,7 @@
         mWindowManager.notifyAppRelaunchingFinished(token);
         final ActivityRecord r = ActivityRecord.isInStackLocked(token);
         if (r != null) {
-            if (r.getStack().shouldSleepOrShutDownActivities()) {
+            if (r.getActivityStack().shouldSleepOrShutDownActivities()) {
                 r.setSleeping(true, true);
             }
         }
@@ -4621,21 +2441,6 @@
         }
     }
 
-    void setDockedStackMinimized(boolean minimized) {
-        // Get currently focused stack before setting mIsDockMinimized. We do this because if
-        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
-        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
-        final ActivityStack current = getTopDisplayFocusedStack();
-        mIsDockMinimized = minimized;
-        if (mIsDockMinimized) {
-            if (current.inSplitScreenPrimaryWindowingMode()) {
-                // The primary split-screen stack can't be focused while it is minimize, so move
-                // focus to something else.
-                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
-            }
-        }
-    }
-
     void wakeUp(String reason) {
         mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
     }
@@ -4654,10 +2459,8 @@
         mDeferResumeCount--;
     }
 
-    /**
-     * @return True if resume can be called.
-     */
-    private boolean readyToResume() {
+    /** @return True if resume can be called. */
+    boolean readyToResume() {
         return mDeferResumeCount == 0;
     }
 
@@ -4709,7 +2512,7 @@
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
                     synchronized (mService.mGlobalLock) {
-                        resumeFocusedStacksTopActivitiesLocked();
+                        mRootActivityContainer.resumeFocusedStacksTopActivities();
                     }
                 } break;
                 case SLEEP_TIMEOUT_MSG: {
@@ -4745,19 +2548,6 @@
         }
     }
 
-    ActivityStack findStackBehind(ActivityStack stack) {
-        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
-        if (display != null) {
-            for (int i = display.getChildCount() - 1; i >= 0; i--) {
-                if (display.getChildAt(i) == stack && i > 0) {
-                    return display.getChildAt(i - 1);
-                }
-            }
-        }
-        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
-                + " in=" + display);
-    }
-
     /**
      * Puts a task into resizing mode during the next app transition.
      *
@@ -4802,8 +2592,8 @@
                 mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false);
             }
 
-            task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE,
-                    activityOptions, ON_TOP);
+            task = mRootActivityContainer.anyTaskForId(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
             if (task == null) {
                 continueUpdateRecentsHomeStackBounds();
                 mWindowManager.executeAppTransition();
@@ -4816,7 +2606,8 @@
                 // from whatever is started from the recents activity, so move the home stack
                 // forward.
                 // TODO (b/115289124): Multi-display supports for recents.
-                getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
+                mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront(
+                        "startActivityFromRecents");
             }
 
             // If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4825,7 +2616,8 @@
                     && task.getRootActivity() != null) {
                 final ActivityRecord targetActivity = task.getTopActivity();
 
-                sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
+                mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                        true /* forceSend */, targetActivity);
                 mActivityMetricsLogger.notifyActivityLaunching(task.intent);
                 try {
                     mService.moveTaskToFrontLocked(task.taskId, 0, options,
@@ -4878,35 +2670,6 @@
     }
 
     /**
-     * @return a list of activities which are the top ones in each visible stack. The first
-     * entry will be the focused activity.
-     */
-    List<IBinder> getTopVisibleActivities() {
-        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
-        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
-        // Traverse all displays.
-        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
-            final ActivityDisplay display = mActivityDisplays.get(i);
-            // Traverse all stacks on a display.
-            for (int j = display.getChildCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getChildAt(j);
-                // Get top activity from a visible stack and add it to the list.
-                if (stack.shouldBeVisible(null /* starting */)) {
-                    final ActivityRecord top = stack.getTopActivity();
-                    if (top != null) {
-                        if (stack == topFocusedStack) {
-                            topActivityTokens.add(0, top.appToken);
-                        } else {
-                            topActivityTokens.add(top.appToken);
-                        }
-                    }
-                }
-            }
-        }
-        return topActivityTokens;
-    }
-
-    /**
      * Internal container to store a match qualifier alongside a WaitResult.
      */
     static class WaitInfo {
@@ -4944,30 +2707,4 @@
             mResult.dump(pw, prefix);
         }
     }
-
-    private final class SleepTokenImpl extends SleepToken {
-        private final String mTag;
-        private final long mAcquireTime;
-        private final int mDisplayId;
-
-        public SleepTokenImpl(String tag, int displayId) {
-            mTag = tag;
-            mDisplayId = displayId;
-            mAcquireTime = SystemClock.uptimeMillis();
-        }
-
-        @Override
-        public void release() {
-            synchronized (mService.mGlobalLock) {
-                removeSleepTokenLocked(this);
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "{\"" + mTag + "\", display " + mDisplayId
-                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
-        }
-    }
-
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index ee5a43c..b8442a8 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -70,6 +70,7 @@
 
     private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
+    private final RootActivityContainer mRootActivityContainer;
     private final Context mServiceContext;
 
     // UserManager cannot be final as it's not ready when this class is instantiated during boot
@@ -102,14 +103,15 @@
 
     ActivityStartInterceptor(
             ActivityTaskManagerService service, ActivityStackSupervisor supervisor) {
-        this(service, supervisor, service.mContext);
+        this(service, supervisor, service.mRootActivityContainer, service.mContext);
     }
 
     @VisibleForTesting
     ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor,
-            Context context) {
+            RootActivityContainer root, Context context) {
         mService = service;
         mSupervisor = supervisor;
+        mRootActivityContainer = root;
         mServiceContext = context;
     }
 
@@ -279,11 +281,11 @@
             mActivityOptions = ActivityOptions.makeBasic();
         }
 
-        ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
-        if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
-            // Showing credential confirmation activity in home task to avoid stopping multi-windowed
-            // mode after showing the full-screen credential confirmation activity.
-            mActivityOptions.setLaunchTaskId(homeActivityRecord.getTask().taskId);
+        ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity();
+        if (homeActivityRecord != null && homeActivityRecord.getTaskRecord() != null) {
+            // Showing credential confirmation activity in home task to avoid stopping
+            // multi-windowed mode after showing the full-screen credential confirmation activity.
+            mActivityOptions.setLaunchTaskId(homeActivityRecord.getTaskRecord().taskId);
         }
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 90f3ff8..bc2136e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -98,6 +98,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -108,6 +109,7 @@
 import android.util.EventLog;
 import android.util.Pools.SynchronizedPool;
 import android.util.Slog;
+import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
@@ -137,6 +139,7 @@
     private static final int INVALID_LAUNCH_MODE = -1;
 
     private final ActivityTaskManagerService mService;
+    private final RootActivityContainer mRootActivityContainer;
     private final ActivityStackSupervisor mSupervisor;
     private final ActivityStartInterceptor mInterceptor;
     private final ActivityStartController mController;
@@ -421,6 +424,7 @@
             ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
         mController = controller;
         mService = service;
+        mRootActivityContainer = service.mRootActivityContainer;
         mSupervisor = supervisor;
         mInterceptor = interceptor;
         reset(true);
@@ -617,7 +621,7 @@
         ActivityRecord sourceRecord = null;
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
-            sourceRecord = mSupervisor.isInAnyStackLocked(resultTo);
+            sourceRecord = mRootActivityContainer.isInAnyStack(resultTo);
             if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                     "Will send result to " + resultTo + " " + sourceRecord);
             if (sourceRecord != null) {
@@ -674,7 +678,7 @@
         }
 
         if (err == ActivityManager.START_SUCCESS && sourceRecord != null
-                && sourceRecord.getTask().voiceSession != null) {
+                && sourceRecord.getTaskRecord().voiceSession != null) {
             // If this activity is being launched as part of a voice session, we need
             // to ensure that it is safe to do so.  If the upcoming activity will also
             // be part of the voice session, we can only launch it if it has explicitly
@@ -714,7 +718,8 @@
             }
         }
 
-        final ActivityStack resultStack = resultRecord == null ? null : resultRecord.getStack();
+        final ActivityStack resultStack = resultRecord == null
+                ? null : resultRecord.getActivityStack();
 
         if (err != START_SUCCESS) {
             if (resultRecord != null) {
@@ -731,6 +736,13 @@
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
+        // not sure if we need to create START_ABORTED_BACKGROUND so for now piggybacking
+        // on START_ABORTED
+        if (!abort) {
+            abort |= shouldAbortBackgroundActivityStart(callingUid, callingPackage, realCallingUid,
+                    callerApp);
+        }
+
         // Merge the two options bundles, while realCallerOptions takes precedence.
         ActivityOptions checkedOptions = options != null
                 ? options.getOptions(intent, aInfo, callerApp, mSupervisor) : null;
@@ -774,6 +786,8 @@
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
             ActivityOptions.abort(checkedOptions);
+            maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp,
+                    null /*r*/, originatingPendingIntent, true /*abortedStart*/);
             return START_ABORTED;
         }
 
@@ -811,7 +825,8 @@
                         null /*profilerInfo*/);
 
                 if (DEBUG_PERMISSIONS_REVIEW) {
-                    final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+                    final ActivityStack focusedStack =
+                            mRootActivityContainer.getTopDisplayFocusedStack();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
                             + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
@@ -847,7 +862,7 @@
             r.appTimeTracker = sourceRecord.appTimeTracker;
         }
 
-        final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
 
         // If we are starting an activity that is not from the same uid as the currently resumed
         // one, check whether app switches are allowed.
@@ -866,19 +881,60 @@
         mController.doPendingActivityLaunches(false);
 
         maybeLogActivityStart(callingUid, callingPackage, realCallingUid, intent, callerApp, r,
-                originatingPendingIntent);
+                originatingPendingIntent, false /*abortedStart*/);
 
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                 true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
+    private boolean shouldAbortBackgroundActivityStart(int callingUid, final String callingPackage,
+            int realCallingUid, WindowProcessController callerApp) {
+        if (mService.isBackgroundActivityStartsEnabled()) {
+            return false;
+        }
+        // don't abort for the most important UIDs
+        if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+            return false;
+        }
+        // don't abort if the callerApp has any visible activity
+        if (callerApp != null && callerApp.hasForegroundActivities()) {
+            return false;
+        }
+        // don't abort if the callingUid is in the foreground
+        if (isUidForeground(callingUid)) {
+            return false;
+        }
+        // don't abort if the realCallingUid is in the foreground and callingUid isn't
+        if ((realCallingUid != callingUid) && isUidForeground(realCallingUid)) {
+            return false;
+        }
+        // don't abort if the caller has the same uid as the recents component
+        if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
+            return false;
+        }
+        // anything that has fallen through will currently be aborted
+        // TODO: remove this toast after feature development is done
+        mService.mUiHandler.post(() -> {
+            Toast.makeText(mService.mContext,
+                    "Blocking background activity start for " + callingPackage,
+                    Toast.LENGTH_SHORT).show();
+        });
+        return true;
+    }
+
+    /** Returns true if uid has a visible window or its process is in top or persistent state. */
+    private boolean isUidForeground(int uid) {
+        return (mService.getUidStateLocked(uid) <= ActivityManager.PROCESS_STATE_TOP)
+            || mService.mWindowManager.isAnyWindowVisibleForUid(uid);
+    }
+
     private void maybeLogActivityStart(int callingUid, String callingPackage, int realCallingUid,
             Intent intent, WindowProcessController callerApp, ActivityRecord r,
-            PendingIntentRecord originatingPendingIntent) {
+            PendingIntentRecord originatingPendingIntent, boolean abortedStart) {
         boolean callerAppHasForegroundActivity =
                 callerApp != null && callerApp.hasForegroundActivities();
         if (!mService.isActivityStartsLoggingEnabled() || callerAppHasForegroundActivity
-                || r == null) {
+                || (!abortedStart && r == null)) {
             // skip logging in this case
             return;
         }
@@ -894,8 +950,8 @@
             final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid)
                     ? callingUidHasAnyVisibleWindow
                     : mService.mWindowManager.isAnyWindowVisibleForUid(realCallingUid);
-            final String targetPackage = r.packageName;
-            final int targetUid = (r.appInfo != null) ? r.appInfo.uid : -1;
+            final String targetPackage = (r != null) ? r.packageName : null;
+            final int targetUid = (r!= null) ? ((r.appInfo != null) ? r.appInfo.uid : -1) : -1;
             final int targetUidProcState = mService.getUidStateLocked(targetUid);
             final boolean targetUidHasAnyVisibleWindow = (targetUid != -1)
                     ? mService.mWindowManager.isAnyWindowVisibleForUid(targetUid)
@@ -1063,7 +1119,7 @@
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
         synchronized (mService.mGlobalLock) {
-            final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack();
+            final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
             stack.mConfigWillChange = globalConfig != null
                     && mService.getGlobalConfiguration().diff(globalConfig) != 0;
             if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -1179,7 +1235,7 @@
                     }
                     case START_DELIVERED_TO_TOP: {
                         outResult.timeout = false;
-                        outResult.who = r.realActivity;
+                        outResult.who = r.mActivityComponent;
                         outResult.totalTime = 0;
                         break;
                     }
@@ -1188,11 +1244,12 @@
                         // in the resumed state.
                         if (r.nowVisible && r.isState(RESUMED)) {
                             outResult.timeout = false;
-                            outResult.who = r.realActivity;
+                            outResult.who = r.mActivityComponent;
                             outResult.totalTime = 0;
                         } else {
                             final long startTimeMs = SystemClock.uptimeMillis();
-                            mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs);
+                            mSupervisor.waitActivityVisible(
+                                    r.mActivityComponent, outResult, startTimeMs);
                             // Note: the timeout variable is not currently not ever set.
                             do {
                                 try {
@@ -1238,7 +1295,7 @@
             result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, outActivity);
         } finally {
-            final ActivityStack currentStack = r.getStack();
+            final ActivityStack currentStack = r.getActivityStack();
             startedActivityStack = currentStack != null ? currentStack : mTargetStack;
 
             if (ActivityManager.isStartResultSuccessful(result)) {
@@ -1249,7 +1306,8 @@
                     final ActivityRecord currentTop =
                             startedActivityStack.topRunningActivityLocked();
                     if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
-                        mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(),
+                        mRootActivityContainer.ensureVisibilityAndConfig(
+                                currentTop, currentTop.getDisplayId(),
                                 true /* markFrozenIfConfigChanged */, false /* deferResume */);
                     }
                 }
@@ -1257,7 +1315,7 @@
                 // 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 without a window container.
-                final ActivityStack stack = mStartActivity.getStack();
+                final ActivityStack stack = mStartActivity.getActivityStack();
                 if (stack != null) {
                     stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
                             null /* intentResultData */, "startActivity", true /* oomAdj */);
@@ -1284,7 +1342,7 @@
         // Do not start home activity if it cannot be launched on preferred display. We are not
         // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
         // fallback to launch on other displays.
-        if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info,
+        if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info,
                 mPreferredDisplayId, true /* allowInstrumenting */)) {
             Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
             return START_CANCELED;
@@ -1302,7 +1360,8 @@
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
             // the device would otherwise leave the locked task.
-            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
+            if (mService.getLockTaskController().isLockTaskModeViolation(
+                    reusedActivity.getTaskRecord(),
                     (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                 Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
@@ -1319,14 +1378,14 @@
             // If mStartActivity does not have a task associated with it, associate it with the
             // reused activity's task. Do not do so if we're clearing top and resetting for a
             // standard launchMode activity.
-            if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
-                mStartActivity.setTask(reusedActivity.getTask());
+            if (mStartActivity.getTaskRecord() == null && !clearTopAndResetStandardLaunchMode) {
+                mStartActivity.setTask(reusedActivity.getTaskRecord());
             }
 
-            if (reusedActivity.getTask().intent == null) {
+            if (reusedActivity.getTaskRecord().intent == null) {
                 // This task was started because of movement of the activity based on affinity...
                 // Now that we are actually launching it, we can assign the base intent.
-                reusedActivity.getTask().setIntent(mStartActivity);
+                reusedActivity.getTaskRecord().setIntent(mStartActivity);
             }
 
             // This code path leads to delivering a new intent, we want to make sure we schedule it
@@ -1335,7 +1394,7 @@
             if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                     || isDocumentLaunchesIntoExisting(mLaunchFlags)
                     || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-                final TaskRecord task = reusedActivity.getTask();
+                final TaskRecord task = reusedActivity.getTaskRecord();
 
                 // 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
@@ -1347,7 +1406,7 @@
                 // the {@code ActivityRecord} removing its reference to the {@code TaskRecord}. The
                 // task reference is needed in the call below to
                 // {@link setTargetStackAndMoveToFrontIfNeeded}.
-                if (reusedActivity.getTask() == null) {
+                if (reusedActivity.getTaskRecord() == null) {
                     reusedActivity.setTask(task);
                 }
 
@@ -1355,13 +1414,14 @@
                     if (top.frontOfTask) {
                         // Activity aliases may mean we use different intents for the top activity,
                         // so make sure the task now has the identity of the new intent.
-                        top.getTask().setIntent(mStartActivity);
+                        top.getTaskRecord().setIntent(mStartActivity);
                     }
                     deliverNewIntent(top);
                 }
             }
 
-            mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity);
+            mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded
+                    (false /* forceSend */, reusedActivity);
 
             reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);
 
@@ -1401,7 +1461,7 @@
 
         if (mStartActivity.packageName == null) {
             final ActivityStack sourceStack = mStartActivity.resultTo != null
-                    ? mStartActivity.resultTo.getStack() : null;
+                    ? mStartActivity.resultTo.getActivityStack() : null;
             if (sourceStack != null) {
                 sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
                         mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
@@ -1413,12 +1473,12 @@
 
         // If the activity being launched is the same as the one currently at the top, then
         // we need to check if it should only be launched once.
-        final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack();
         final ActivityRecord topFocused = topStack.getTopActivity();
         final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
         final boolean dontStart = top != null && mStartActivity.resultTo == null
-                && top.realActivity.equals(mStartActivity.realActivity)
-                && top.userId == mStartActivity.userId
+                && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+                && top.mUserId == mStartActivity.mUserId
                 && top.attachedToProcess()
                 && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                 || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
@@ -1430,7 +1490,7 @@
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
             if (mDoResume) {
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
             }
             ActivityOptions.abort(mOptions);
             if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1443,7 +1503,7 @@
 
             // Don't use mStartActivity.task to show the toast. We're not starting a new activity
             // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
-            mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode,
+            mSupervisor.handleNonResizableTaskIfNeeded(top.getTaskRecord(), preferredWindowingMode,
                     mPreferredDisplayId, topStack);
 
             return START_DELIVERED_TO_TOP;
@@ -1451,7 +1511,7 @@
 
         boolean newTask = false;
         final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
-                ? mSourceRecord.getTask() : null;
+                ? mSourceRecord.getTaskRecord() : null;
 
         // Should this be considered a new task?
         int result = START_SUCCESS;
@@ -1473,25 +1533,26 @@
         }
 
         mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
-                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
+                mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
         mService.getPackageManagerInternalLocked().grantEphemeralAccess(
-                mStartActivity.userId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
+                mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
                 UserHandle.getAppId(mCallingUid));
         if (newTask) {
-            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.userId,
-                    mStartActivity.getTask().taskId);
+            EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, mStartActivity.mUserId,
+                    mStartActivity.getTaskRecord().taskId);
         }
         ActivityStack.logStartActivity(
-                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask());
+                EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTaskRecord());
         mTargetStack.mLastPausedActivity = null;
 
-        mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity);
+        mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                false /* forceSend */, mStartActivity);
 
         mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
                 mOptions);
         if (mDoResume) {
             final ActivityRecord topTaskActivity =
-                    mStartActivity.getTask().topRunningActivityLocked();
+                    mStartActivity.getTaskRecord().topRunningActivityLocked();
             if (!mTargetStack.isFocusable()
                     || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                     && mStartActivity != topTaskActivity)) {
@@ -1512,19 +1573,19 @@
                 // task stack to be focusable, then ensure that we now update the focused stack
                 // accordingly.
                 if (mTargetStack.isFocusable()
-                        && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
+                        && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
                     mTargetStack.moveToFront("startActivityUnchecked");
                 }
-                mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
-                        mOptions);
+                mRootActivityContainer.resumeFocusedStacksTopActivities(
+                        mTargetStack, mStartActivity, mOptions);
             }
         } else if (mStartActivity != null) {
-            mSupervisor.mRecentTasks.add(mStartActivity.getTask());
+            mSupervisor.mRecentTasks.add(mStartActivity.getTaskRecord());
         }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+        mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
 
-        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
-                mPreferredDisplayId, mTargetStack);
+        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTaskRecord(),
+                preferredWindowingMode, mPreferredDisplayId, mTargetStack);
 
         return START_SUCCESS;
     }
@@ -1642,7 +1703,7 @@
             if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
                 r.mTaskOverlay = true;
                 if (!mOptions.canTaskOverlayResume()) {
-                    final TaskRecord task = mSupervisor.anyTaskForIdLocked(
+                    final TaskRecord task = mRootActivityContainer.anyTaskForId(
                             mOptions.getLaunchTaskId());
                     final ActivityRecord top = task != null ? task.getTopActivity() : null;
                     if (top != null && !top.isState(RESUMED)) {
@@ -1678,10 +1739,10 @@
         if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
             ActivityRecord checkedCaller = sourceRecord;
             if (checkedCaller == null) {
-                checkedCaller = mSupervisor.getTopDisplayFocusedStack()
+                checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack()
                         .topRunningNonDelayedActivityLocked(mNotTop);
             }
-            if (!checkedCaller.realActivity.equals(r.realActivity)) {
+            if (!checkedCaller.mActivityComponent.equals(r.mActivityComponent)) {
                 // Caller is not the same as launcher, so always needed.
                 mStartFlags &= ~START_FLAG_ONLY_IF_NEEDED;
             }
@@ -1692,7 +1753,7 @@
 
     private void sendNewTaskResultRequestIfNeeded() {
         final ActivityStack sourceStack = mStartActivity.resultTo != null
-                ? mStartActivity.resultTo.getStack() : null;
+                ? mStartActivity.resultTo.getActivityStack() : null;
         if (sourceStack != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // For whatever reason this activity is being launched into a new task...
             // yet the caller has requested a result back.  Well, that is pretty messed up,
@@ -1796,7 +1857,7 @@
             return;
         }
         if (!mSourceRecord.finishing) {
-            mSourceStack = mSourceRecord.getStack();
+            mSourceStack = mSourceRecord.getActivityStack();
             return;
         }
 
@@ -1814,7 +1875,7 @@
             // example, if this method is being called for processing a pending activity launch, it
             // is possible that the activity has been removed from the task after the launch was
             // enqueued.
-            final TaskRecord sourceTask = mSourceRecord.getTask();
+            final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
             mNewTaskIntent = sourceTask != null ? sourceTask.intent : null;
         }
         mSourceRecord = null;
@@ -1840,22 +1901,23 @@
         putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
         ActivityRecord intentActivity = null;
         if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
-            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId());
             intentActivity = task != null ? task.getTopActivity() : null;
         } else if (putIntoExistingTask) {
             if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
                 // There can be one and only one instance of single instance activity in the
                 // history, and it is always in its own unique task, so we do a special search.
-               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+               intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                        mStartActivity.isActivityTypeHome());
             } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                 // For the launch adjacent case we only want to put the activity in an existing
                 // task if the activity already exists in the history.
-                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
+                intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info,
                         !(LAUNCH_SINGLE_TASK == mLaunchMode));
             } else {
                 // Otherwise find the best task to put the activity in.
-                intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
+                intentActivity =
+                        mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId);
             }
         }
 
@@ -1876,7 +1938,7 @@
      * @return {@link ActivityRecord} brought to front.
      */
     private ActivityRecord setTargetStackAndMoveToFrontIfNeeded(ActivityRecord intentActivity) {
-        mTargetStack = intentActivity.getStack();
+        mTargetStack = intentActivity.getActivityStack();
         mTargetStack.mLastPausedActivity = null;
         // If the target task is not in the front, then we need to bring it to the front...
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
@@ -1887,9 +1949,9 @@
             final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
-            final TaskRecord topTask = curTop != null ? curTop.getTask() : null;
+            final TaskRecord topTask = curTop != null ? curTop.getTaskRecord() : null;
             differentTopTask = topTask != null
-                    && (topTask != intentActivity.getTask() || topTask != focusStack.topTask());
+                    && (topTask != intentActivity.getTaskRecord() || topTask != focusStack.topTask());
         } else {
             // The existing task should always be different from those in other displays.
             differentTopTask = true;
@@ -1898,10 +1960,11 @@
         if (differentTopTask && !mAvoidMoveToFront) {
             mStartActivity.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
             if (mSourceRecord == null || (mSourceStack.getTopActivity() != null &&
-                    mSourceStack.getTopActivity().getTask() == mSourceRecord.getTask())) {
+                    mSourceStack.getTopActivity().getTaskRecord()
+                            == mSourceRecord.getTaskRecord())) {
                 // We really do want to push this one into the user's face, right now.
                 if (mLaunchTaskBehind && mSourceRecord != null) {
-                    intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
+                    intentActivity.setTaskToAffiliateWith(mSourceRecord.getTaskRecord());
                 }
 
                 // If the launch flags carry both NEW_TASK and CLEAR_TASK, the task's activities
@@ -1915,8 +1978,8 @@
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
                 if (!willClearTask) {
                     final ActivityStack launchStack = getLaunchStack(
-                            mStartActivity, mLaunchFlags, mStartActivity.getTask(), mOptions);
-                    final TaskRecord intentTask = intentActivity.getTask();
+                            mStartActivity, mLaunchFlags, mStartActivity.getTaskRecord(), mOptions);
+                    final TaskRecord intentTask = intentActivity.getTaskRecord();
                     if (launchStack == null || launchStack == mTargetStack) {
                         // We only want to move to the front, if we aren't going to launch on a
                         // different stack. If we launch on a different stack, we will put the
@@ -1946,7 +2009,7 @@
                         // Target and computed stacks are on different displays and we've
                         // found a matching task - move the existing instance to that display and
                         // move it to front.
-                        intentActivity.getTask().reparent(launchStack, ON_TOP,
+                        intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
                                 REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                 "reparentToDisplay");
                         mMovedToFront = true;
@@ -1956,7 +2019,7 @@
                         // For example, the activity may have been initially started with an intent
                         // which placed it in the fullscreen stack. To ensure the proper handling of
                         // the activity based on home stack assumptions, we must move it over.
-                        intentActivity.getTask().reparent(launchStack, ON_TOP,
+                        intentActivity.getTaskRecord().reparent(launchStack, ON_TOP,
                                 REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                 "reparentingHome");
                         mMovedToFront = true;
@@ -1972,14 +2035,14 @@
         }
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
-        mTargetStack = intentActivity.getStack();
+        mTargetStack = intentActivity.getActivityStack();
         if (!mMovedToFront && mDoResume) {
             if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + mTargetStack
                     + " from " + intentActivity);
             mTargetStack.moveToFront("intentActivityFound");
         }
 
-        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
+        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTaskRecord(),
                 WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
 
         // If the caller has requested that the target task be reset, then do so.
@@ -2001,14 +2064,14 @@
             // launching another activity.
             // TODO(b/36119896):  We shouldn't trigger activity launches in this path since we are
             // already launching one.
-            final TaskRecord task = intentActivity.getTask();
+            final TaskRecord task = intentActivity.getTaskRecord();
             task.performClearTaskLocked();
             mReuseTask = task;
             mReuseTask.setIntent(mStartActivity);
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                 || isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
-            ActivityRecord top = intentActivity.getTask().performClearTaskLocked(mStartActivity,
-                    mLaunchFlags);
+            final ActivityRecord top = intentActivity.getTaskRecord().performClearTaskLocked(
+                    mStartActivity, mLaunchFlags);
             if (top == null) {
                 // A special case: we need to start the activity because it is not currently
                 // running, and the caller has asked to clear the current task to have this
@@ -2020,7 +2083,7 @@
                 // Now pretend like this activity is being started by the top of its task, so it
                 // is put in the right place.
                 mSourceRecord = intentActivity;
-                final TaskRecord task = mSourceRecord.getTask();
+                final TaskRecord task = mSourceRecord.getTaskRecord();
                 if (task != null && task.getStack() == null) {
                     // Target stack got cleared when we all activities were removed above.
                     // Go ahead and reset it.
@@ -2030,19 +2093,21 @@
                             !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
                 }
             }
-        } else if (mStartActivity.realActivity.equals(intentActivity.getTask().realActivity)) {
+        } else if (mStartActivity.mActivityComponent.equals(
+                intentActivity.getTaskRecord().realActivity)) {
             // In this case the top activity on the task is the same as the one being launched,
             // so we take that as a request to bring the task to the foreground. If the top
             // activity in the task is the root activity, deliver this new intent to it if it
             // desires.
             if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                         || LAUNCH_SINGLE_TOP == mLaunchMode)
-                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+                    && intentActivity.mActivityComponent.equals(
+                            mStartActivity.mActivityComponent)) {
                 if (intentActivity.frontOfTask) {
-                    intentActivity.getTask().setIntent(mStartActivity);
+                    intentActivity.getTaskRecord().setIntent(mStartActivity);
                 }
                 deliverNewIntent(intentActivity);
-            } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
+            } else if (!intentActivity.getTaskRecord().isSameIntentFilter(mStartActivity)) {
                 // In this case we are launching the root activity of the task, but with a
                 // different intent. We should start a new instance on top.
                 mAddingToTask = true;
@@ -2055,23 +2120,23 @@
             // current task.
             mAddingToTask = true;
             mSourceRecord = intentActivity;
-        } else if (!intentActivity.getTask().rootWasReset) {
+        } else if (!intentActivity.getTaskRecord().rootWasReset) {
             // In this case we are launching into an existing task that has not yet been started
             // from its front door. The current task has been brought to the front. Ideally,
             // we'd probably like to place this new task at the bottom of its stack, but that's
             // a little hard to do with the current organization of the code so for now we'll
             // just drop it.
-            intentActivity.getTask().setIntent(mStartActivity);
+            intentActivity.getTaskRecord().setIntent(mStartActivity);
         }
     }
 
     private void resumeTargetStackIfNeeded() {
         if (mDoResume) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
+            mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions);
         } else {
             ActivityOptions.abort(mOptions);
         }
-        mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
+        mRootActivityContainer.updateUserStack(mStartActivity.mUserId, mTargetStack);
     }
 
     private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) {
@@ -2082,16 +2147,16 @@
 
         if (mReuseTask == null) {
             final TaskRecord task = mTargetStack.createTaskRecord(
-                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId),
+                    mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
                     mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                     mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                     mVoiceInteractor, !mLaunchTaskBehind /* toTop */, mStartActivity, mSourceRecord,
                     mOptions);
             addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
-            updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds);
+            updateBounds(mStartActivity.getTaskRecord(), mLaunchParams.mBounds);
 
             if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                    + " in new task " + mStartActivity.getTask());
+                    + " in new task " + mStartActivity.getTaskRecord());
         } else {
             addOrReparentStartingActivity(mReuseTask, "setTaskFromReuseOrCreateNewTask");
         }
@@ -2100,7 +2165,8 @@
             mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
         }
 
-        if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
+        if (mService.getLockTaskController().isLockTaskModeViolation(
+                mStartActivity.getTaskRecord())) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
             return START_RETURN_LOCK_TASK_MODE_VIOLATION;
         }
@@ -2116,20 +2182,21 @@
             return;
         }
 
-        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
         activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                 mStartActivity.launchedFromPackage);
         mIntentDelivered = true;
     }
 
     private int setTaskFromSourceRecord() {
-        if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
+        if (mService.getLockTaskController().isLockTaskModeViolation(
+                mSourceRecord.getTaskRecord())) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
             return START_RETURN_LOCK_TASK_MODE_VIOLATION;
         }
 
-        final TaskRecord sourceTask = mSourceRecord.getTask();
-        final ActivityStack sourceStack = mSourceRecord.getStack();
+        final TaskRecord sourceTask = mSourceRecord.getTaskRecord();
+        final ActivityStack sourceStack = mSourceRecord.getActivityStack();
         // We only want to allow changing stack in two cases:
         // 1. If the target task is not the top one. Otherwise we would move the launching task to
         //    the other side, rather than show two side by side.
@@ -2139,19 +2206,19 @@
         final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
                 || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
         if (moveStackAllowed) {
-            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
-                    mOptions);
+            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags,
+                    mStartActivity.getTaskRecord(), mOptions);
             // If target stack is not found now - we can't just rely on the source stack, as it may
             // be not suitable. Let's check other displays.
             if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
                 // Can't use target display, lets find a stack on the source display.
-                mTargetStack = mSupervisor.getValidLaunchStackOnDisplay(
+                mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay(
                         sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams);
             }
             if (mTargetStack == null) {
                 // There are no suitable stacks on the target and source display(s). Look on all
                 // displays.
-                mTargetStack = mSupervisor.getNextValidLaunchStackLocked(
+                mTargetStack = mRootActivityContainer.getNextValidLaunchStack(
                         mStartActivity, -1 /* currentFocus */);
             }
         }
@@ -2177,12 +2244,12 @@
             ActivityRecord top = sourceTask.performClearTaskLocked(mStartActivity, mLaunchFlags);
             mKeepCurTransition = true;
             if (top != null) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
+                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTaskRecord());
                 deliverNewIntent(top);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
                 ActivityOptions.abort(mOptions);
                 return START_DELIVERED_TO_TOP;
@@ -2193,14 +2260,14 @@
             // stack if so.
             final ActivityRecord top = sourceTask.findActivityInHistoryLocked(mStartActivity);
             if (top != null) {
-                final TaskRecord task = top.getTask();
+                final TaskRecord task = top.getTaskRecord();
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
                 ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
                 deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
-                    mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
                 return START_DELIVERED_TO_TOP;
             }
@@ -2210,7 +2277,8 @@
         // the same task as the one that is starting it.
         addOrReparentStartingActivity(sourceTask, "setTaskFromSourceRecord");
         if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in existing task " + mStartActivity.getTask() + " from source " + mSourceRecord);
+                + " in existing task " + mStartActivity.getTaskRecord()
+                + " from source " + mSourceRecord);
         return START_SUCCESS;
     }
 
@@ -2227,8 +2295,8 @@
         // Check whether we should actually launch the new activity in to the task,
         // or just reuse the current activity on top.
         ActivityRecord top = mInTask.getTopActivity();
-        if (top != null && top.realActivity.equals(mStartActivity.realActivity)
-                && top.userId == mStartActivity.userId) {
+        if (top != null && top.mActivityComponent.equals(mStartActivity.mActivityComponent)
+                && top.mUserId == mStartActivity.mUserId) {
             if ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                     || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK)) {
                 mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
@@ -2254,7 +2322,8 @@
 
         if (!mLaunchParams.mBounds.isEmpty()) {
             // TODO: Shouldn't we already know what stack to use by the time we get here?
-            ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
+            ActivityStack stack = mRootActivityContainer.getLaunchStack(
+                    null, null, mInTask, ON_TOP);
             if (stack != mInTask.getStack()) {
                 mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
                         DEFER_RESUME, "inTaskToFront");
@@ -2269,7 +2338,7 @@
 
         addOrReparentStartingActivity(mInTask, "setTaskFromInTask");
         if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in explicit task " + mStartActivity.getTask());
+                + " in explicit task " + mStartActivity.getTaskRecord());
 
         return START_SUCCESS;
     }
@@ -2295,17 +2364,18 @@
             mTargetStack.moveToFront("addingToTopTask");
         }
         final ActivityRecord prev = mTargetStack.getTopActivity();
-        final TaskRecord task = (prev != null) ? prev.getTask() : mTargetStack.createTaskRecord(
-                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.userId), mStartActivity.info,
+        final TaskRecord task = (prev != null)
+                ? prev.getTaskRecord() : mTargetStack.createTaskRecord(
+                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId), mStartActivity.info,
                 mIntent, null, null, true, mStartActivity, mSourceRecord, mOptions);
         addOrReparentStartingActivity(task, "setTaskToCurrentTopOrCreateNewTask");
         mTargetStack.positionChildWindowContainerAtTop(task);
         if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
-                + " in new guessed " + mStartActivity.getTask());
+                + " in new guessed " + mStartActivity.getTaskRecord());
     }
 
     private void addOrReparentStartingActivity(TaskRecord parent, String reason) {
-        if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) {
+        if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
             parent.addActivityToTop(mStartActivity);
         } else {
             mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
@@ -2341,14 +2411,14 @@
 
     private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
             ActivityOptions aOptions) {
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
         if (stack != null) {
             return stack;
         }
 
         final ActivityStack currentStack = task != null ? task.getStack() : null;
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         if (currentStack != null) {
             if (focusedStack != currentStack) {
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
@@ -2369,18 +2439,18 @@
 
         if (mPreferredDisplayId != DEFAULT_DISPLAY) {
             // Try to put the activity in a stack on a secondary display.
-            stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions,
-                    mLaunchParams);
+            stack = mRootActivityContainer.getValidLaunchStackOnDisplay(
+                    mPreferredDisplayId, r, aOptions, mLaunchParams);
             if (stack == null) {
                 // If source display is not suitable - look for topmost valid stack in the system.
                 if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
                         "computeStackFocus: Can't launch on mPreferredDisplayId="
                                 + mPreferredDisplayId + ", looking on all displays.");
-                stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId);
+                stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
             }
         }
         if (stack == null) {
-            stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+            stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
         }
         if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
                 + r + " stackId=" + stack.mStackId);
@@ -2390,7 +2460,7 @@
     /** Check if provided activity record can launch in currently focused stack. */
     // TODO: This method can probably be consolidated into getLaunchStack() below.
     private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         final boolean canUseFocusedStack;
         if (focusedStack.isActivityTypeAssistant()) {
             canUseFocusedStack = r.isActivityTypeAssistant();
@@ -2436,14 +2506,14 @@
             // full resolution.
             mLaunchParams.mPreferredDisplayId =
                     mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY;
-            final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP,
-                    mLaunchParams);
+            final ActivityStack stack =
+                    mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams);
             mLaunchParams.mPreferredDisplayId = mPreferredDisplayId;
             return stack;
         }
         // Otherwise handle adjacent launch.
 
-        final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack();
         // The parent activity doesn't want to launch the activity on top of itself, but
         // instead tries to put it onto other side in side-by-side mode.
         final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
@@ -2461,7 +2531,8 @@
             if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
                 // If parent was in docked stack, the natural place to launch another activity
                 // will be fullscreen, so it can appear alongside the docked window.
-                final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
+                final int activityType =
+                        mRootActivityContainer.resolveActivityType(r, mOptions, task);
                 return parentStack.getDisplay().getOrCreateStack(
                         WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
             } else {
@@ -2469,10 +2540,10 @@
                 // and if yes, we will launch into that stack. If not, we just put the new
                 // activity into parent's stack, because we can't find a better place.
                 final ActivityStack dockedStack =
-                        mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                        mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
                 if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
                     // There is a docked stack, but it isn't visible, so we can't launch into that.
-                    return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP);
+                    return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP);
                 } else {
                     return dockedStack;
                 }
@@ -2660,7 +2731,7 @@
         prefix = prefix + "  ";
         pw.print(prefix);
         pw.print("mCurrentUser=");
-        pw.println(mSupervisor.mCurrentUser);
+        pw.println(mRootActivityContainer.mCurrentUser);
         pw.print(prefix);
         pw.print("mLastStartReason=");
         pw.println(mLastStartReason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d665592..0cdbedb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -473,4 +473,6 @@
     public abstract void setProfileApp(String profileApp);
     public abstract void setProfileProc(WindowProcessController wpc);
     public abstract void setProfilerInfo(ProfilerInfo profilerInfo);
+
+    public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4f01d699..0967afd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -91,8 +91,6 @@
         .PACKAGE;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
@@ -122,6 +120,8 @@
 import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
@@ -190,6 +190,7 @@
 import android.os.IBinder;
 import android.os.IUserManager;
 import android.os.LocaleList;
+import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
@@ -246,7 +247,6 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
-import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
@@ -341,7 +341,8 @@
     ActivityManagerInternal mAmInternal;
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
-    private ActivityTaskManagerInternal mInternal;
+    @VisibleForTesting
+    final ActivityTaskManagerInternal mInternal;
     PowerManagerInternal mPowerManagerInternal;
     private UsageStatsManagerInternal mUsageStatsInternal;
 
@@ -351,6 +352,7 @@
     /* Global service lock used by the package the owns this service. */
     final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
     ActivityStackSupervisor mStackSupervisor;
+    RootActivityContainer mRootActivityContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
     private AppOpsService mAppOpsService;
@@ -643,6 +645,7 @@
         mSystemThread = ActivityThread.currentActivityThread();
         mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
+        mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
     }
 
@@ -750,9 +753,9 @@
         return mGlobalLock;
     }
 
-    public void setActivityManagerService(IntentFirewall intentFirewall,
-            PendingIntentController intentController) {
-        mH = new H();
+    public void initialize(IntentFirewall intentFirewall, PendingIntentController intentController,
+            Looper looper) {
+        mH = new H(looper);
         mUiHandler = new UiHandler();
         mIntentFirewall = intentFirewall;
         final File systemDir = SystemServiceManager.ensureSystemDir();
@@ -764,7 +767,8 @@
         mTempConfig.setLocales(LocaleList.getDefault());
         mConfigurationSeq = mTempConfig.seq = 1;
         mStackSupervisor = createStackSupervisor();
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
+        mRootActivityContainer = new RootActivityContainer(this);
+        mRootActivityContainer.onConfigurationChanged(mTempConfig);
 
         mTaskChangeNotificationController =
                 new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
@@ -799,6 +803,7 @@
             mWindowManager = wm;
             mLockTaskController.setWindowManager(wm);
             mStackSupervisor.setWindowManager(wm);
+            mRootActivityContainer.setWindowManager(wm);
         }
     }
 
@@ -893,7 +898,6 @@
     }
 
     private void start() {
-        mInternal = new LocalService();
         LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
     }
 
@@ -1254,7 +1258,7 @@
                 sourceToken = resultTo;
             }
 
-            sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
+            sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken);
             if (sourceRecord == null) {
                 throw new SecurityException("Called with bad activity token: " + sourceToken);
             }
@@ -1438,7 +1442,7 @@
                 return true;
             }
             // Keep track of the root activity of the task before we finish it
-            TaskRecord tr = r.getTask();
+            final TaskRecord tr = r.getTaskRecord();
             ActivityRecord rootR = tr.getRootActivity();
             if (rootR == null) {
                 Slog.w(TAG, "Finishing task with all activities already finished");
@@ -1453,7 +1457,7 @@
             // We should consolidate.
             if (mController != null) {
                 // Find the first activity that is not finishing.
-                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+                final ActivityRecord next = r.getActivityStack().topRunningActivityLocked(token, 0);
                 if (next != null) {
                     // ask watcher if this is allowed
                     boolean resumeOK = true;
@@ -1515,7 +1519,7 @@
 
                 // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
                 // can finish.
-                final TaskRecord task = r.getTask();
+                final TaskRecord task = r.getTaskRecord();
                 if (getLockTaskController().activityBlockedFromFinish(r)) {
                     return false;
                 }
@@ -1798,7 +1802,7 @@
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(true);
                 if (translucentChanged) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 }
                 mWindowManager.setAppFullscreen(token, true);
                 return translucentChanged;
@@ -1818,7 +1822,7 @@
                 if (r == null) {
                     return false;
                 }
-                final TaskRecord task = r.getTask();
+                final TaskRecord task = r.getTaskRecord();
                 int index = task.mActivities.lastIndexOf(r);
                 if (index > 0) {
                     ActivityRecord under = task.mActivities.get(index - 1);
@@ -1826,9 +1830,9 @@
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(false);
                 if (translucentChanged) {
-                    r.getStack().convertActivityToTranslucent(r);
+                    r.getActivityStack().convertActivityToTranslucent(r);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.setAppFullscreen(token, false);
                 return translucentChanged;
             }
@@ -1841,9 +1845,9 @@
     public void notifyActivityDrawn(IBinder token) {
         if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
         synchronized (mGlobalLock) {
-            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+            ActivityRecord r = mRootActivityContainer.isInAnyStack(token);
             if (r != null) {
-                r.getStack().notifyActivityDrawnLocked(r);
+                r.getActivityStack().notifyActivityDrawnLocked(r);
             }
         }
     }
@@ -1878,7 +1882,7 @@
             synchronized (mGlobalLock) {
                 ActivityStack focusedStack = getTopDisplayFocusedStack();
                 if (focusedStack != null) {
-                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+                    return mRootActivityContainer.getStackInfo(focusedStack.mStackId);
                 }
                 return null;
             }
@@ -1894,14 +1898,14 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
                     return;
                 }
                 final ActivityRecord r = stack.topRunningActivityLocked();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         } finally {
@@ -1916,14 +1920,14 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     return;
                 }
                 final ActivityRecord r = task.topRunningActivityLocked();
                 if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         } finally {
@@ -1963,7 +1967,7 @@
         synchronized (mGlobalLock) {
             final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
             if (srec != null) {
-                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+                return srec.getActivityStack().shouldUpRecreateTaskLocked(srec, destAffinity);
             }
         }
         return false;
@@ -1976,7 +1980,8 @@
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
             if (r != null) {
-                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+                return r.getActivityStack().navigateUpToLocked(
+                        r, destIntent, resultCode, resultData);
             }
             return false;
         }
@@ -2008,7 +2013,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task != null) {
                     return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
                 }
@@ -2026,7 +2031,7 @@
         Rect rect = new Rect();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (task == null) {
                     Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
@@ -2057,7 +2062,7 @@
         synchronized (mGlobalLock) {
             enforceCallerIsRecentsOrHasPermission(
                     MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
-            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+            final TaskRecord tr = mRootActivityContainer.anyTaskForId(id,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr != null) {
                 return tr.lastTaskDescription;
@@ -2077,7 +2082,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
@@ -2166,7 +2171,7 @@
         }
         final long origId = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
             if (task == null) {
                 Slog.d(TAG, "Could not find task for id: "+ taskId);
                 SafeActivityOptions.abort(options);
@@ -2283,7 +2288,7 @@
 
             final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
                     callingUid);
-            mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType,
+            mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
                     ignoreWindowingMode, callingUid, allowed);
         }
 
@@ -2296,7 +2301,7 @@
             final long origId = Binder.clearCallingIdentity();
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
-                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+                r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode);
             }
             Binder.restoreCallingIdentity(origId);
         }
@@ -2319,7 +2324,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task == null) {
                     Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
                     return;
@@ -2328,7 +2333,7 @@
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                         + " to stackId=" + stackId + " toTop=" + toTop);
 
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     throw new IllegalStateException(
                             "moveTaskToStack: No stack for stackId=" + stackId);
@@ -2358,7 +2363,7 @@
         try {
             synchronized (mGlobalLock) {
                 if (animate) {
-                    final PinnedActivityStack stack = mStackSupervisor.getStack(stackId);
+                    final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
@@ -2370,12 +2375,12 @@
                     stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
                             animationDuration, false /* fromFullscreen */);
                 } else {
-                    final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                     if (stack == null) {
                         Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
                         return;
                     }
-                    mStackSupervisor.resizeStackLocked(stack, destBounds,
+                    mRootActivityContainer.resizeStack(stack, destBounds,
                             null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                             preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
                 }
@@ -2409,7 +2414,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
@@ -2451,7 +2456,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mStackSupervisor.removeStacksInWindowingModes(windowingModes);
+                mRootActivityContainer.removeStacksInWindowingModes(windowingModes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2466,7 +2471,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mStackSupervisor.removeStacksWithActivityTypes(activityTypes);
+                mRootActivityContainer.removeStacksWithActivityTypes(activityTypes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2497,7 +2502,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getAllStackInfosLocked();
+                return mRootActivityContainer.getAllStackInfos();
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2510,7 +2515,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getStackInfo(windowingMode, activityType);
+                return mRootActivityContainer.getStackInfo(windowingMode, activityType);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2541,7 +2546,7 @@
             if (r == null) {
                 return;
             }
-            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+            startLockTaskModeLocked(r.getTaskRecord(), false /* isSystemCaller */);
         }
     }
 
@@ -2552,7 +2557,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     return;
@@ -2574,7 +2579,7 @@
             if (r == null) {
                 return;
             }
-            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+            stopLockTaskModeInternal(r.getTaskRecord(), false /* isSystemCaller */);
         }
     }
 
@@ -2594,7 +2599,7 @@
             return;
         }
 
-        final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
         if (stack == null || task != stack.topTask()) {
             throw new IllegalArgumentException("Invalid task, not in foreground");
         }
@@ -2609,7 +2614,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // When a task is locked, dismiss the pinned stack if it exists
-            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+            mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
 
             getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
         } finally {
@@ -2667,7 +2672,7 @@
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
                 r.setTaskDescription(td);
-                final TaskRecord task = r.getTask();
+                final TaskRecord task = r.getTaskRecord();
                 task.updateTaskDescription();
                 mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
             }
@@ -2711,7 +2716,7 @@
             try {
                 // TODO: VI Consider treating local voice interactions and voice tasks
                 // differently here
-                mStackSupervisor.finishVoiceTask(session);
+                mRootActivityContainer.finishVoiceTask(session);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -2723,7 +2728,7 @@
     public boolean isTopOfTask(IBinder token) {
         synchronized (mGlobalLock) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            return r != null && r.getTask().getTopActivity() == r;
+            return r != null && r.getTaskRecord().getTopActivity() == r;
         }
     }
 
@@ -2762,8 +2767,8 @@
             }
             if (structure != null) {
                 // Pre-fill the task/activity component for all assist data receivers
-                structure.setTaskId(pae.activity.getTask().taskId);
-                structure.setActivityComponent(pae.activity.realActivity);
+                structure.setTaskId(pae.activity.getTaskRecord().taskId);
+                structure.setActivityComponent(pae.activity.mActivityComponent);
                 structure.setHomeActivity(pae.isHome);
             }
             pae.haveResult = true;
@@ -2871,9 +2876,9 @@
                                     + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
                 }
 
-                final ActivityStack stack = r.getStack();
+                final ActivityStack stack = r.getActivityStack();
                 final TaskRecord task = stack.createTaskRecord(
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), ainfo, intent,
                         null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
@@ -2901,7 +2906,7 @@
     @Override
     public void setTaskResizeable(int taskId, int resizeableMode) {
         synchronized (mGlobalLock) {
-            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+            final TaskRecord task = mRootActivityContainer.anyTaskForId(
                     taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (task == null) {
                 Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
@@ -2917,7 +2922,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
@@ -2969,7 +2974,7 @@
                 if (r == null) {
                     return false;
                 }
-                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+                return r.getActivityStack().safelyDestroyActivityLocked(r, "app-req");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -2982,7 +2987,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 final WindowProcessController app = getProcessController(appInt);
-                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+                mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -3076,7 +3081,7 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
                 if (stack == null) {
                     Slog.w(TAG, "removeStack: No stack with id=" + stackId);
                     return;
@@ -3101,7 +3106,7 @@
             try {
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
                         + " to displayId=" + displayId);
-                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+                mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3119,7 +3124,7 @@
                             "exitFreeformMode: No activity record matching token=" + token);
                 }
 
-                final ActivityStack stack = r.getStack();
+                final ActivityStack stack = r.getActivityStack();
                 if (stack == null || !stack.inFreeformWindowingMode()) {
                     throw new IllegalStateException(
                             "exitFreeformMode: You can only go fullscreen from freeform.");
@@ -3405,7 +3410,7 @@
             if (activity == null) {
                 return false;
             }
-            userId = activity.userId;
+            userId = activity.mUserId;
         }
         return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
     }
@@ -3563,13 +3568,13 @@
             try {
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
                         + taskId + " in stackId=" + stackId + " at position=" + position);
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId);
                 if (task == null) {
                     throw new IllegalArgumentException("positionTaskInStack: no task for id="
                             + taskId);
                 }
 
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
 
                 if (stack == null) {
                     throw new IllegalArgumentException("positionTaskInStack: no stack for id="
@@ -3624,7 +3629,7 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                        mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
                     return;
@@ -3634,7 +3639,7 @@
                     // Caller wants the current split-screen primary stack to be the top stack after
                     // it goes fullscreen, so move it to the front.
                     stack.moveToFront("dismissSplitScreenMode");
-                } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) {
+                } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) {
                     // In this case the current split-screen primary stack shouldn't be the top
                     // stack after it goes fullscreen, but it current has focus, so we move the
                     // focus to the top-most split-screen secondary stack next to it.
@@ -3665,7 +3670,7 @@
         try {
             synchronized (mGlobalLock) {
                 final PinnedActivityStack stack =
-                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+                        mRootActivityContainer.getDefaultDisplay().getPinnedStack();
                 if (stack == null) {
                     Slog.w(TAG, "dismissPip: pinned stack not found.");
                     return;
@@ -3707,7 +3712,7 @@
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                final ActivityStack stack = mRootActivityContainer.getStack(fromStackId);
                 if (stack != null){
                     if (!stack.isActivityTypeStandardOrUndefined()) {
                         throw new IllegalArgumentException(
@@ -3742,7 +3747,7 @@
 
             long ident = Binder.clearCallingIdentity();
             try {
-                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+                return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3779,14 +3784,14 @@
     }
 
     private boolean isInPictureInPictureMode(ActivityRecord r) {
-        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
-                || r.getStack().isInStackLocked(r) == null) {
+        if (r == null || r.getActivityStack() == null || !r.inPinnedWindowingMode()
+                || r.getActivityStack().isInStackLocked(r) == null) {
             return false;
         }
 
         // If we are animating to fullscreen then we have already dispatched the PIP mode
         // changed, so we should reflect that check here as well.
-        final PinnedActivityStack stack = r.getStack();
+        final PinnedActivityStack stack = r.getActivityStack();
         final PinnedStackWindowController windowController = stack.getWindowContainerController();
         return !windowController.isAnimatingBoundsToFullscreen();
     }
@@ -3820,9 +3825,9 @@
                         // Adjust the source bounds by the insets for the transition down
                         final Rect sourceBounds = new Rect(
                                 r.pictureInPictureArgs.getSourceRectHint());
-                        mStackSupervisor.moveActivityToPinnedStackLocked(
+                        mRootActivityContainer.moveActivityToPinnedStack(
                                 r, sourceBounds, aspectRatio, "enterPictureInPictureMode");
-                        final PinnedActivityStack stack = r.getStack();
+                        final PinnedActivityStack stack = r.getActivityStack();
                         stack.setPictureInPictureAspectRatio(aspectRatio);
                         stack.setPictureInPictureActions(actions);
                         MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
@@ -3866,7 +3871,7 @@
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
                     // be used the next time the activity enters PiP
-                    final PinnedActivityStack stack = r.getStack();
+                    final PinnedActivityStack stack = r.getActivityStack();
                     if (!stack.isAnimatingBoundsToFullscreen()) {
                         stack.setPictureInPictureAspectRatio(
                                 r.pictureInPictureArgs.getAspectRatio());
@@ -3924,8 +3929,8 @@
         }
 
         if (params.hasSetAspectRatio()
-                && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
-                params.getAspectRatio())) {
+                && !mWindowManager.isValidPictureInPictureAspectRatio(
+                        r.getActivityStack().mDisplayId, params.getAspectRatio())) {
             final float minAspectRatio = mContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
             final float maxAspectRatio = mContext.getResources().getFloat(
@@ -4011,7 +4016,7 @@
         }
 
         int err;
-        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+        if ((err = vrService.hasVrPackage(packageName, r.mUserId)) !=
                 VrManagerInternal.NO_ERROR) {
             return err;
         }
@@ -4041,7 +4046,7 @@
             if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
                 throw new SecurityException("Only focused activity can call startVoiceInteraction");
             }
-            if (mRunningVoice != null || activity.getTask().voiceSession != null
+            if (mRunningVoice != null || activity.getTaskRecord().voiceSession != null
                     || activity.voiceSession != null) {
                 Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
                 return;
@@ -4099,7 +4104,7 @@
 
         synchronized (mGlobalLock) {
             // Check if display is initialized in AM.
-            if (!mStackSupervisor.isDisplayAdded(displayId)) {
+            if (!mRootActivityContainer.isDisplayAdded(displayId)) {
                 // Call might come when display is not yet added or has already been removed.
                 if (DEBUG_CONFIGURATION) {
                     Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
@@ -4189,7 +4194,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
@@ -4209,7 +4214,7 @@
         try {
             final TaskRecord task;
             synchronized (mGlobalLock) {
-                task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                task = mRootActivityContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (task == null) {
                     Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
@@ -4251,7 +4256,7 @@
             if (mLastResumedActivity == null) {
                 return getCurrentUserId();
             }
-            return mLastResumedActivity.userId;
+            return mLastResumedActivity.mUserId;
         }
     }
 
@@ -4429,9 +4434,9 @@
         if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
             Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
                     + " to main stack for VR");
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+            final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
                     WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
-            moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
+            moveTaskToStack(r.getTaskRecord().taskId, stack.mStackId, true /* toTop */);
         }
         mH.post(() -> {
             if (!mVrController.onVrModeChanged(r)) {
@@ -4443,7 +4448,7 @@
                 if (disableNonVrUi) {
                     // If we are in a VR mode where Picture-in-Picture mode is unsupported,
                     // then remove the pinned stack.
-                    mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                    mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
                 }
             }
         });
@@ -4495,7 +4500,7 @@
     }
 
     ActivityStack getTopDisplayFocusedStack() {
-        return mStackSupervisor.getTopDisplayFocusedStack();
+        return mRootActivityContainer.getTopDisplayFocusedStack();
     }
 
     /** Pokes the task persister. */
@@ -4507,6 +4512,21 @@
         return mKeyguardController.isKeyguardLocked();
     }
 
+    /**
+     * Clears launch params for the given package.
+     * @param packageNames the names of the packages of which the launch params are to be cleared
+     */
+    @Override
+    public void clearLaunchParamsForPackages(List<String> packageNames) {
+        mAmInternal.enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS,
+                "clearLaunchParamsForPackages");
+        synchronized (mGlobalLock) {
+            for (int i = 0; i < packageNames.size(); ++i) {
+                mStackSupervisor.mLaunchParamsPersister.removeRecordForPackage(packageNames.get(i));
+            }
+        }
+    }
+
     void dumpLastANRLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
         if (mLastANRState == null) {
@@ -4556,12 +4576,12 @@
             int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) {
         pw.println(header);
 
-        boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient,
+        boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient,
                 dumpPackage);
         boolean needSep = printedAnything;
 
         boolean printed = ActivityStackSupervisor.printThisActivity(pw,
-                mStackSupervisor.getTopResumedActivity(),  dumpPackage, needSep,
+                mRootActivityContainer.getTopResumedActivity(),  dumpPackage, needSep,
                 "  ResumedActivity: ");
         if (printed) {
             printedAnything = true;
@@ -4583,7 +4603,7 @@
 
     void dumpActivityContainersLocked(PrintWriter pw) {
         pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)");
-        mStackSupervisor.dumpChildrenNames(pw, " ");
+        mRootActivityContainer.dumpChildrenNames(pw, " ");
         pw.println(" ");
     }
 
@@ -4607,7 +4627,7 @@
         ArrayList<ActivityRecord> activities;
 
         synchronized (mGlobalLock) {
-            activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly,
+            activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly,
                     dumpFocusedStackOnly);
         }
 
@@ -4627,7 +4647,7 @@
             }
             needSep = true;
             synchronized (mGlobalLock) {
-                final TaskRecord task = r.getTask();
+                final TaskRecord task = r.getTaskRecord();
                 if (lastTask != task) {
                     lastTask = task;
                     pw.print("TASK "); pw.print(lastTask.affinity);
@@ -4682,7 +4702,7 @@
     }
 
     void writeSleepStateToProto(ProtoOutputStream proto) {
-        for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) {
+        for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) {
             proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS,
                     st.toString());
         }
@@ -4727,7 +4747,7 @@
      * also corresponds to the merged configuration of the default display.
      */
     Configuration getGlobalConfiguration() {
-        return mStackSupervisor.getConfiguration();
+        return mRootActivityContainer.getConfiguration();
     }
 
     boolean updateConfigurationLocked(Configuration values, ActivityRecord starting,
@@ -4859,7 +4879,7 @@
         mTempConfig.seq = increaseConfigurationSeqLocked();
 
         // Update stored global config and notify everyone about the change.
-        mStackSupervisor.onConfigurationChanged(mTempConfig);
+        mRootActivityContainer.onConfigurationChanged(mTempConfig);
 
         Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
         // TODO(multi-display): Update UsageEvents#Event to include displayId.
@@ -4906,7 +4926,7 @@
 
         // Override configuration of the default display duplicates global config, so we need to
         // update it also. This will also notify WindowManager about changes.
-        performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
+        performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume,
                 DEFAULT_DISPLAY);
 
         return changes;
@@ -4960,12 +4980,12 @@
 
     private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,
             int displayId) {
-        mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));
+        mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId));
         final int changes = mTempConfig.updateFrom(values);
         if (changes != 0) {
             Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
                     + mTempConfig + " for displayId=" + displayId);
-            mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId);
+            mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId);
 
             final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
             if (isDensityChange && displayId == DEFAULT_DISPLAY) {
@@ -5015,6 +5035,10 @@
         return mAmInternal.isActivityStartsLoggingEnabled();
     }
 
+    boolean isBackgroundActivityStartsEnabled() {
+        return mAmInternal.isBackgroundActivityStartsEnabled();
+    }
+
     void enableScreenAfterBoot(boolean booted) {
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                 SystemClock.uptimeMillis());
@@ -5087,7 +5111,7 @@
 
     /** Update AMS states when an activity is resumed. */
     void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         if (task.isActivityTypeStandard()) {
             if (mCurAppTimeTracker != r.appTimeTracker) {
                 // We are switching app tracking.  Complete the current one.
@@ -5095,7 +5119,7 @@
                     mCurAppTimeTracker.stop();
                     mH.obtainMessage(
                             REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget();
-                    mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker);
+                    mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker);
                     mCurAppTimeTracker = null;
                 }
                 if (r.appTimeTracker != null) {
@@ -5119,7 +5143,7 @@
             if (mLastResumedActivity != null) {
                 final IVoiceInteractionSession session;
 
-                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTask();
+                final TaskRecord lastResumedActivityTask = mLastResumedActivity.getTaskRecord();
                 if (lastResumedActivityTask != null
                         && lastResumedActivityTask.voiceSession != null) {
                     session = lastResumedActivityTask.voiceSession;
@@ -5137,8 +5161,8 @@
             }
         }
 
-        if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) {
-            mAmInternal.sendForegroundProfileChanged(r.userId);
+        if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
+            mAmInternal.sendForegroundProfileChanged(r.mUserId);
         }
         updateResumedAppTrace(r);
         mLastResumedActivity = r;
@@ -5149,21 +5173,22 @@
         applyUpdateVrModeLocked(r);
 
         EventLogTags.writeAmSetResumedActivity(
-                r == null ? -1 : r.userId,
+                r == null ? -1 : r.mUserId,
                 r == null ? "NULL" : r.shortComponentName,
                 reason);
     }
 
     ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) {
         synchronized (mGlobalLock) {
-            final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId);
+            final ActivityTaskManagerInternal.SleepToken token =
+                    mRootActivityContainer.createSleepToken(tag, displayId);
             updateSleepIfNeededLocked();
             return token;
         }
     }
 
     void updateSleepIfNeededLocked() {
-        final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
+        final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay();
         final boolean wasSleeping = mSleeping;
         boolean updateOomAdj = false;
 
@@ -5179,7 +5204,7 @@
                 mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
                 mStackSupervisor.comeOutOfSleepIfNeededLocked();
             }
-            mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */);
+            mRootActivityContainer.applySleepTokens(true /* applyToStacks */);
             if (wasSleeping) {
                 updateOomAdj = true;
             }
@@ -5210,7 +5235,8 @@
 
     void updateUsageStats(ActivityRecord component, boolean resumed) {
         final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::updateUsageStats,
-                mAmInternal, component.realActivity, component.app.mUid, component.userId, resumed);
+                mAmInternal, component.mActivityComponent, component.app.mUid, component.mUserId,
+                resumed);
         mH.sendMessage(m);
     }
 
@@ -5245,7 +5271,7 @@
         mHeavyWeightProcess = root.app;
         final Message m = PooledLambda.obtainMessage(
                 ActivityTaskManagerService::postHeavyWeightProcessNotification, this,
-                root.app, root.intent, root.userId);
+                root.app, root.intent, root.mUserId);
         mH.sendMessage(m);
     }
 
@@ -5355,7 +5381,7 @@
 
     // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
     private void startTimeTrackingFocusedActivityLocked() {
-        final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
+        final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity();
         if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) {
             mCurAppTimeTracker.start(resumedActivity.packageName);
         }
@@ -5380,7 +5406,7 @@
     /** Applies latest configuration and/or visibility updates if needed. */
     private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
         boolean kept = true;
-        final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack();
+        final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack();
         // mainStack is null during startup.
         if (mainStack != null) {
             if (changes != 0 && starting == null) {
@@ -5395,7 +5421,7 @@
                         false /* preserveWindow */);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
-                mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
+                mRootActivityContainer.ensureActivitiesVisible(starting, changes,
                         !PRESERVE_WINDOWS);
             }
         }
@@ -5567,8 +5593,8 @@
         static final int FIRST_ACTIVITY_STACK_MSG = 100;
         static final int FIRST_SUPERVISOR_STACK_MSG = 200;
 
-        public H() {
-            super(DisplayThread.get().getLooper());
+        H(Looper looper) {
+            super(looper);
         }
 
         @Override
@@ -5611,9 +5637,9 @@
         @Override
         public ComponentName getHomeActivityForUser(int userId) {
             synchronized (mGlobalLock) {
-                ActivityRecord homeActivity =
-                        mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
-                return homeActivity == null ? null : homeActivity.realActivity;
+                final ActivityRecord homeActivity =
+                        mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId);
+                return homeActivity == null ? null : homeActivity.mActivityComponent;
             }
         }
 
@@ -5650,14 +5676,14 @@
         @Override
         public List<IBinder> getTopVisibleActivities() {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.getTopVisibleActivities();
+                return mRootActivityContainer.getTopVisibleActivities();
             }
         }
 
         @Override
         public void notifyDockedStackMinimizedChanged(boolean minimized) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.setDockedStackMinimized(minimized);
+                mRootActivityContainer.setDockedStackMinimized(minimized);
             }
         }
 
@@ -5738,7 +5764,7 @@
                 // We might change the visibilities here, so prepare an empty app transition which
                 // might be overridden later if we actually change visibilities.
                 final ActivityDisplay activityDisplay =
-                        mStackSupervisor.getActivityDisplay(displayId);
+                        mRootActivityContainer.getActivityDisplay(displayId);
                 if (activityDisplay == null) {
                     return;
                 }
@@ -5747,7 +5773,7 @@
                 if (!wasTransitionSet) {
                     dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */);
                 }
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
 
                 // If there was a transition set already we don't want to interfere with it as we
                 // might be starting it too early.
@@ -5764,7 +5790,7 @@
         public void notifyKeyguardTrustedChanged() {
             synchronized (mGlobalLock) {
                 if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 }
             }
         }
@@ -5791,7 +5817,7 @@
                             "setFocusedActivity: No activity record matching token=" + token);
                 }
                 if (r.moveFocusableActivityToTop("setFocusedActivity")) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                 }
             }
         }
@@ -5942,7 +5968,7 @@
         public boolean shuttingDown(boolean booted, int timeout) {
             synchronized (mGlobalLock) {
                 mShuttingDown = true;
-                mStackSupervisor.prepareForShutdownLocked();
+                mRootActivityContainer.prepareForShutdown();
                 updateEventDispatchingLocked(booted);
                 notifyTaskPersisterLocked(null, true);
                 return mStackSupervisor.shutdownLocked(timeout);
@@ -6049,7 +6075,7 @@
         @Override
         public void onPackageReplaced(ApplicationInfo aInfo) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);
+                mRootActivityContainer.updateActivityApplicationInfo(aInfo);
             }
         }
 
@@ -6079,7 +6105,7 @@
             mH.post(() -> {
                 synchronized (mGlobalLock) {
                     final ActivityDisplay activityDisplay =
-                            mStackSupervisor.getActivityDisplay(displayId);
+                            mRootActivityContainer.getActivityDisplay(displayId);
                     if (activityDisplay == null) {
                         // Call might come when display is not yet added or has been removed.
                         if (DEBUG_CONFIGURATION) {
@@ -6107,9 +6133,9 @@
                 int requestCode, int resultCode, Intent data) {
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
-                if (r != null && r.getStack() != null) {
-                    r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
-                            resultCode, data);
+                if (r != null && r.getActivityStack() != null) {
+                    r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
+                            requestCode, resultCode, data);
                 }
             }
         }
@@ -6162,14 +6188,14 @@
         @Override
         public boolean startHomeActivity(int userId, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
+                return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY);
             }
         }
 
         @Override
         public boolean startHomeOnAllDisplays(int userId, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.startHomeOnAllDisplays(userId, reason);
+                return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
             }
         }
 
@@ -6233,7 +6259,7 @@
                 Runnable finishInstrumentationCallback) {
             synchronized (mGlobalLock) {
                 // Remove this application's activities from active lists.
-                boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc);
+                boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc);
 
                 wpc.clearRecentTasks();
                 wpc.clearActivities();
@@ -6245,12 +6271,12 @@
                 mWindowManager.deferSurfaceLayout();
                 try {
                     if (!restarting && hasVisibleActivities
-                            && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
+                            && !mRootActivityContainer.resumeFocusedStacksTopActivities()) {
                         // If there was nothing to resume, and we are not already restarting this
                         // process, but there is a visible activity that is hosted by the process...
                         // then make sure all visible activities are running, taking care of
                         // restarting this process.
-                        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                     }
                 } finally {
                     mWindowManager.continueSurfaceLayout();
@@ -6279,7 +6305,7 @@
                     }
                     mWindowManager.closeSystemDialogs(reason);
 
-                    mStackSupervisor.closeSystemDialogsLocked();
+                    mRootActivityContainer.closeSystemDialogs();
                 }
                 // Call into AM outside the synchronized block.
                 mAmInternal.broadcastCloseSystemDialogs(reason);
@@ -6293,9 +6319,9 @@
                 String packageName, Set<String> disabledClasses, int userId, boolean booted) {
             synchronized (mGlobalLock) {
                 // Clean-up disabled activities.
-                if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
+                if (mRootActivityContainer.finishDisabledPackageActivities(
                         packageName, disabledClasses, true, false, userId) && booted) {
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mRootActivityContainer.resumeFocusedStacksTopActivities();
                     mStackSupervisor.scheduleIdleLocked();
                 }
 
@@ -6312,7 +6338,7 @@
 
                 boolean didSomething =
                         getActivityStartController().clearPendingActivityLaunches(packageName);
-                didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName,
+                didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName,
                         null, doit, evenPersistent, userId);
                 return didSomething;
             }
@@ -6321,7 +6347,7 @@
         @Override
         public void resumeTopActivities(boolean scheduleIdle) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                mRootActivityContainer.resumeFocusedStacksTopActivities();
                 if (scheduleIdle) {
                     mStackSupervisor.scheduleIdleLocked();
                 }
@@ -6338,7 +6364,7 @@
         @Override
         public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.attachApplicationLocked(wpc);
+                return mRootActivityContainer.attachApplication(wpc);
             }
         }
 
@@ -6360,7 +6386,7 @@
                             // Showing launcher to avoid user entering credential twice.
                             startHomeActivity(currentUserId, "notifyLockedProfile");
                         }
-                        mStackSupervisor.lockAllProfileTasks(userId);
+                        mRootActivityContainer.lockAllProfileTasks(userId);
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -6381,9 +6407,9 @@
                     ActivityOptions activityOptions = options != null
                             ? new ActivityOptions(options) : ActivityOptions.makeBasic();
                     final ActivityRecord homeActivity =
-                            mStackSupervisor.getDefaultDisplayHomeActivity();
+                            mRootActivityContainer.getDefaultDisplayHomeActivity();
                     if (homeActivity != null) {
-                        activityOptions.setLaunchTaskId(homeActivity.getTask().taskId);
+                        activityOptions.setLaunchTaskId(homeActivity.getTaskRecord().taskId);
                     }
                     mContext.startActivityAsUser(intent, activityOptions.toBundle(),
                             UserHandle.CURRENT);
@@ -6398,7 +6424,7 @@
             synchronized (mGlobalLock) {
                 // The output proto of "activity --proto activities"
                 // is ActivityManagerServiceDumpActivitiesProto
-                mStackSupervisor.writeToProto(proto,
+                mRootActivityContainer.writeToProto(proto,
                         ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR);
             }
         }
@@ -6493,7 +6519,7 @@
                 }
                 if (dumpPackage == null) {
                     pw.println("  mGlobalConfiguration: " + getGlobalConfiguration());
-                    mStackSupervisor.dumpDisplayConfigs(pw, "  ");
+                    mRootActivityContainer.dumpDisplayConfigs(pw, "  ");
                 }
                 if (dumpAll) {
                     if (dumpPackage == null) {
@@ -6521,7 +6547,7 @@
                 if (dumpPackage == null) {
                     pw.println("  mWakefulness="
                             + PowerManagerInternal.wakefulnessToString(wakefulness));
-                    pw.println("  mSleepTokens=" + mStackSupervisor.mSleepTokens);
+                    pw.println("  mSleepTokens=" + mRootActivityContainer.mSleepTokens);
                     if (mRunningVoice != null) {
                         pw.println("  mRunningVoice=" + mRunningVoice);
                         pw.println("  mVoiceWakeLock" + mVoiceWakeLock);
@@ -6648,14 +6674,14 @@
         @Override
         public boolean canGcNow() {
             synchronized (mGlobalLock) {
-                return isSleeping() || mStackSupervisor.allResumedActivitiesIdle();
+                return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle();
             }
         }
 
         @Override
         public WindowProcessController getTopApp() {
             synchronized (mGlobalLock) {
-                final ActivityRecord top = mStackSupervisor.getTopResumedActivity();
+                final ActivityRecord top = mRootActivityContainer.getTopResumedActivity();
                 return top != null ? top.app : null;
             }
         }
@@ -6663,8 +6689,8 @@
         @Override
         public void rankTaskLayersIfNeeded() {
             synchronized (mGlobalLock) {
-                if (mStackSupervisor != null) {
-                    mStackSupervisor.rankTaskLayersIfNeeded();
+                if (mRootActivityContainer != null) {
+                    mRootActivityContainer.rankTaskLayersIfNeeded();
                 }
             }
         }
@@ -6672,35 +6698,35 @@
         @Override
         public void scheduleDestroyAllActivities(String reason) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.scheduleDestroyAllActivities(null, reason);
+                mRootActivityContainer.scheduleDestroyAllActivities(null, reason);
             }
         }
 
         @Override
         public void removeUser(int userId) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.removeUserLocked(userId);
+                mRootActivityContainer.removeUser(userId);
             }
         }
 
         @Override
         public boolean switchUser(int userId, UserState userState) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.switchUserLocked(userId, userState);
+                return mRootActivityContainer.switchUser(userId, userState);
             }
         }
 
         @Override
         public void onHandleAppCrash(WindowProcessController wpc) {
             synchronized (mGlobalLock) {
-                mStackSupervisor.handleAppCrashLocked(wpc);
+                mRootActivityContainer.handleAppCrash(wpc);
             }
         }
 
         @Override
         public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) {
             synchronized (mGlobalLock) {
-                return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason);
+                return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason);
             }
         }
 
@@ -6870,5 +6896,12 @@
                 mProfilerInfo = profilerInfo;
             }
         }
+
+        @Override
+        public ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry() {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getActivityMetricsLogger().getLaunchObserverRegistry();
+            }
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 04fef02..441c593 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 
 import android.app.ActivityManager;
 import android.app.IAppTask;
@@ -77,7 +77,7 @@
         synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (tr == null) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -115,7 +115,7 @@
         TaskRecord tr;
         IApplicationThread appThread;
         synchronized (mService.mGlobalLock) {
-            tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+            tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                     MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
             if (tr == null) {
                 throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
@@ -143,7 +143,7 @@
         synchronized (mService.mGlobalLock) {
             long origId = Binder.clearCallingIdentity();
             try {
-                TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId,
+                TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId,
                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
                 if (tr == null) {
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 32a6f74..bf00ffb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -305,7 +305,7 @@
             AppWindowToken wtoken = openingApps.valueAt(i);
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
 
-            if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
+            if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) {
                 // This token isn't going to be animating. Add it to the list of tokens to
                 // be notified of app transition complete since the notification will not be
                 // sent be the app window animator.
@@ -341,7 +341,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
             // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
             //       animating?
-            wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
+            wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction);
             wtoken.updateReportedVisibilityLocked();
             // Force the allDrawn flag, because we want to start
             // this guy's animations regardless of whether it's
@@ -350,9 +350,8 @@
             wtoken.deferClearAllDrawn = false;
             // Ensure that apps that are mid-starting are also scheduled to have their
             // starting windows removed after the animation is complete
-            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
-                    && wtoken.getController() != null) {
-                wtoken.getController().removeStartingWindow();
+            if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) {
+                wtoken.removeStartingWindow();
             }
 
             if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 0436857..6c3fbc1 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -123,7 +123,8 @@
 
         // TODO(b/75318890): Need to move this to when the app actually crashes.
         if (/*ActivityManager.isRunningInTestHarness()
-                &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
+                &&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(
+                        r.mActivityComponent)) {
             // Don't show warning if we are running in a test harness and we don't have to always
             // show for this activity.
             return;
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
deleted file mode 100644
index bd1460a..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ /dev/null
@@ -1,914 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
-import static android.app.ActivityOptions.ANIM_CUSTOM;
-import static android.app.ActivityOptions.ANIM_NONE;
-import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
-import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
-import static android.app.ActivityOptions.ANIM_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
-import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.app.ActivityManager.TaskSnapshot;
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.content.ComponentName;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Rect;
-import android.os.Debug;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Slog;
-import android.view.AppTransitionAnimationSpec;
-import android.view.IAppTransitionAnimationSpecsFuture;
-import android.view.IApplicationToken;
-import android.view.RemoteAnimationDefinition;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.AttributeCache;
-import com.android.server.policy.WindowManagerPolicy.StartingSurface;
-
-/**
- * Controller for the app window token container. This is created by activity manager to link
- * activity records to the app window token container they use in window manager.
- *
- * Test class: {@link AppWindowContainerControllerTests}
- */
-public class AppWindowContainerController
-        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
-
-    private static final int STARTING_WINDOW_TYPE_NONE = 0;
-    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
-    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
-
-    private final IApplicationToken mToken;
-    private final Handler mHandler;
-
-    private final class H extends Handler {
-        public static final int NOTIFY_WINDOWS_DRAWN = 1;
-        public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2;
-        public static final int NOTIFY_WINDOWS_NOTDRAWN = 3;
-
-        public H(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case NOTIFY_WINDOWS_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(true /* drawn */, msg.getWhen());
-                    break;
-                case NOTIFY_STARTING_WINDOW_DRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onStartingWindowDrawn(msg.getWhen());
-                    break;
-                case NOTIFY_WINDOWS_NOTDRAWN:
-                    if (mListener == null) {
-                        return;
-                    }
-                    if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in "
-                            + AppWindowContainerController.this.mToken);
-                    mListener.onWindowsDrawn(false /* drawn */, msg.getWhen());
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private final Runnable mOnWindowsVisible = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsVisible();
-    };
-
-    private final Runnable mOnWindowsGone = () -> {
-        if (mListener == null) {
-            return;
-        }
-        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in "
-                + AppWindowContainerController.this.mToken);
-        mListener.onWindowsGone();
-    };
-
-    private final Runnable mAddStartingWindow = new Runnable() {
-
-        @Override
-        public void run() {
-            final StartingData startingData;
-            final AppWindowToken container;
-
-            synchronized (mGlobalLock) {
-                if (mContainer == null) {
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
-                            + " add starting window");
-                    return;
-                }
-
-                // There can only be one adding request, silly caller!
-                mService.mAnimationHandler.removeCallbacks(this);
-
-                startingData = mContainer.startingData;
-                container = mContainer;
-            }
-
-            if (startingData == null) {
-                // Animation has been canceled... do nothing.
-                if (DEBUG_STARTING_WINDOW)
-                    Slog.v(TAG_WM, "startingData was nulled out before handling"
-                            + " mAddStartingWindow: " + mContainer);
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                    + AppWindowContainerController.this + ": startingData="
-                    + container.startingData);
-
-            StartingSurface surface = null;
-            try {
-                surface = startingData.createStartingSurface(container);
-            } catch (Exception e) {
-                Slog.w(TAG_WM, "Exception when adding starting window", e);
-            }
-            if (surface != null) {
-                boolean abort = false;
-                synchronized (mGlobalLock) {
-                    // If the window was successfully added, then
-                    // we need to remove it.
-                    if (container.removed || container.startingData == null) {
-                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                                "Aborted starting " + container
-                                        + ": removed=" + container.removed
-                                        + " startingData=" + container.startingData);
-                        container.startingWindow = null;
-                        container.startingData = null;
-                        abort = true;
-                    } else {
-                        container.startingSurface = surface;
-                    }
-                    if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                            "Added starting " + mContainer
-                                    + ": startingWindow="
-                                    + container.startingWindow + " startingView="
-                                    + container.startingSurface);
-                }
-                if (abort) {
-                    surface.remove();
-                }
-            } else if (DEBUG_STARTING_WINDOW) {
-                Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
-            }
-        }
-    };
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) {
-        this(taskController, token, activityComponent, listener, index, requestedOrientation,
-                fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind,
-                alwaysFocusable, targetSdkVersion, rotationAnimationHint,
-                inputDispatchingTimeoutNanos, WindowManagerService.getInstance());
-    }
-
-    public AppWindowContainerController(TaskWindowContainerController taskController,
-            IApplicationToken token, ComponentName activityComponent,
-            AppWindowContainerListener listener, int index, int requestedOrientation,
-            boolean fullscreen, boolean showForAllUsers, int configChanges,
-            boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable,
-            int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos,
-            WindowManagerService service) {
-        super(listener, service);
-        mHandler = new H(service.mH.getLooper());
-        mToken = token;
-        synchronized (mGlobalLock) {
-            AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder());
-            if (atoken != null) {
-                // TODO: Should this throw an exception instead?
-                Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken);
-                return;
-            }
-
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("AppWindowContainerController: invalid "
-                        + " controller=" + taskController);
-            }
-
-            atoken = createAppWindow(mService, token, activityComponent, voiceInteraction,
-                    task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen,
-                    showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint,
-                    configChanges, launchTaskBehind, alwaysFocusable, this);
-            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken
-                    + " controller=" + taskController + " at " + index);
-            task.addChild(atoken, index);
-        }
-    }
-
-    @VisibleForTesting
-    AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-            ComponentName component, boolean voiceInteraction, DisplayContent dc,
-            long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
-            int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-            boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
-        return new AppWindowToken(service, token, component, voiceInteraction, dc,
-                inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation,
-                rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                controller);
-    }
-
-    public void removeContainer(int displayId) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
-            if (dc == null) {
-                Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: "
-                        + mToken + " from non-existing displayId=" + displayId);
-                return;
-            }
-            dc.removeAppToken(mToken.asBinder());
-            super.removeContainer();
-        }
-    }
-
-    @Override
-    public void removeContainer() {
-        throw new UnsupportedOperationException("Use removeContainer(displayId) instead.");
-    }
-
-    public void reparent(TaskWindowContainerController taskController, int position) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken
-                    + " to task=" + taskController + " at " + position);
-            if (mContainer == null) {
-                if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM,
-                        "reparent: could not find app token=" + mToken);
-                return;
-            }
-            final Task task = taskController.mContainer;
-            if (task == null) {
-                throw new IllegalArgumentException("reparent: could not find task="
-                        + taskController);
-            }
-            mContainer.reparent(task, position);
-            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
-        }
-    }
-
-    public Configuration setOrientation(int requestedOrientation, int displayId,
-            Configuration displayConfig, boolean freezeScreenIfNeeded) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to set orientation of non-existing app token: " + mToken);
-                return null;
-            }
-
-            mContainer.setOrientation(requestedOrientation);
-
-            final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;
-            return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);
-
-        }
-    }
-
-    public int getOrientation() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return SCREEN_ORIENTATION_UNSPECIFIED;
-            }
-
-            return mContainer.getOrientationIgnoreVisibility();
-        }
-    }
-
-    public void setDisablePreviewScreenshots(boolean disable) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.setDisablePreviewScreenshots(disable);
-        }
-    }
-
-    public void setVisibility(boolean visible, boolean deferHidingClient) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
-                        + mToken);
-                return;
-            }
-
-            final AppWindowToken wtoken = mContainer;
-            final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition;
-
-            // Don't set visibility to false if we were already not visible. This prevents WM from
-            // adding the app to the closing app list which doesn't make sense for something that is
-            // already not visible. However, set visibility to true even if we are already visible.
-            // This makes sure the app is added to the opening apps list so that the right
-            // transition can be selected.
-            // TODO: Probably a good idea to separate the concept of opening/closing apps from the
-            // concept of setting visibility...
-            if (!visible && wtoken.hiddenRequested) {
-
-                if (!deferHidingClient && wtoken.mDeferHidingClient) {
-                    // We previously deferred telling the client to hide itself when visibility was
-                    // initially set to false. Now we would like it to hide, so go ahead and set it.
-                    wtoken.mDeferHidingClient = deferHidingClient;
-                    wtoken.setClientHidden(true);
-                }
-                return;
-            }
-
-            if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility("
-                    + mToken + ", visible=" + visible + "): " + appTransition
-                    + " hidden=" + wtoken.isHidden() + " hiddenRequested="
-                    + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
-
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            displayContent.mOpeningApps.remove(wtoken);
-            displayContent.mClosingApps.remove(wtoken);
-            wtoken.waitingToShow = false;
-            wtoken.hiddenRequested = !visible;
-            wtoken.mDeferHidingClient = deferHidingClient;
-
-            if (!visible) {
-                // If the app is dead while it was visible, we kept its dead window on screen.
-                // Now that the app is going invisible, we can remove it. It will be restarted
-                // if made visible again.
-                wtoken.removeDeadWindows();
-            } else {
-                if (!appTransition.isTransitionSet()
-                        && appTransition.isReady()) {
-                    // Add the app mOpeningApps if transition is unset but ready. This means
-                    // we're doing a screen freeze, and the unfreeze will wait for all opening
-                    // apps to be ready.
-                    displayContent.mOpeningApps.add(wtoken);
-                }
-                wtoken.startingMoved = false;
-                // If the token is currently hidden (should be the common case), or has been
-                // stopped, then we need to set up to wait for its windows to be ready.
-                if (wtoken.isHidden() || wtoken.mAppStopped) {
-                    wtoken.clearAllDrawn();
-
-                    // If the app was already visible, don't reset the waitingToShow state.
-                    if (wtoken.isHidden()) {
-                        wtoken.waitingToShow = true;
-                    }
-                }
-
-                // In the case where we are making an app visible but holding off for a transition,
-                // we still need to tell the client to make its windows visible so they get drawn.
-                // Otherwise, we will wait on performing the transition until all windows have been
-                // drawn, they never will be, and we are sad.
-                wtoken.setClientHidden(false);
-
-                wtoken.requestUpdateWallpaperIfNeeded();
-
-                if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken);
-                wtoken.mAppStopped = false;
-
-                mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-            }
-
-            // If we are preparing an app transition, then delay changing
-            // the visibility of this token until we execute that transition.
-            if (wtoken.okToAnimate() && appTransition.isTransitionSet()) {
-                wtoken.inPendingTransaction = true;
-                if (visible) {
-                    displayContent.mOpeningApps.add(wtoken);
-                    wtoken.mEnteringAnimation = true;
-                } else {
-                    displayContent.mClosingApps.add(wtoken);
-                    wtoken.mEnteringAnimation = false;
-                }
-                if (appTransition.getAppTransition()
-                        == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
-                    // We're launchingBehind, add the launching activity to mOpeningApps.
-                    final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
-                    if (win != null) {
-                        final AppWindowToken focusedToken = win.mAppToken;
-                        if (focusedToken != null) {
-                            if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
-                                    + " adding " + focusedToken + " to mOpeningApps");
-                            // Force animation to be loaded.
-                            focusedToken.setHidden(true);
-                            displayContent.mOpeningApps.add(focusedToken);
-                        }
-                    }
-                }
-                return;
-            }
-
-            wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction);
-            wtoken.updateReportedVisibilityLocked();
-        }
-    }
-
-    /**
-     * Notifies that we launched an app that might be visible or not visible depending on what kind
-     * of Keyguard flags it's going to set on its windows.
-     */
-    public void notifyUnknownVisibilityLaunched() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(
-                        mContainer);
-            }
-        }
-    }
-
-    public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
-            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
-            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
-        synchronized (mGlobalLock) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
-                    + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask
-                    + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning
-                    + " allowTaskSnapshot=" + allowTaskSnapshot);
-
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken);
-                return false;
-            }
-
-            // If the display is frozen, we won't do anything until the actual window is
-            // displayed so there is no reason to put in the starting window.
-            if (!mContainer.okToDisplay()) {
-                return false;
-            }
-
-            if (mContainer.startingData != null) {
-                return false;
-            }
-
-            final WindowState mainWin = mContainer.findMainWindow();
-            if (mainWin != null && mainWin.mWinAnimator.getShown()) {
-                // App already has a visible window...why would you want a starting window?
-                return false;
-            }
-
-            final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot(
-                    mContainer.getTask().mTaskId, mContainer.getTask().mUserId,
-                    false /* restoreFromDisk */, false /* reducedResolution */);
-            final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
-                    allowTaskSnapshot, activityCreated, fromRecents, snapshot);
-
-            if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
-                return createSnapshot(snapshot);
-            }
-
-            // If this is a translucent window, then don't show a starting window -- the current
-            // effect (a full-screen opaque starting window that fades away to the real contents
-            // when it is ready) does not work for this.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x"
-                    + Integer.toHexString(theme));
-            if (theme != 0) {
-                AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
-                        com.android.internal.R.styleable.Window, mService.mCurrentUserId);
-                if (ent == null) {
-                    // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
-                    // see that.
-                    return false;
-                }
-                final boolean windowIsTranslucent = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsTranslucent, false);
-                final boolean windowIsFloating = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowIsFloating, false);
-                final boolean windowShowWallpaper = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowShowWallpaper, false);
-                final boolean windowDisableStarting = ent.array.getBoolean(
-                        com.android.internal.R.styleable.Window_windowDisablePreview, false);
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent
-                        + " Floating=" + windowIsFloating
-                        + " ShowWallpaper=" + windowShowWallpaper);
-                if (windowIsTranslucent) {
-                    return false;
-                }
-                if (windowIsFloating || windowDisableStarting) {
-                    return false;
-                }
-                if (windowShowWallpaper) {
-                    if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget()
-                            == null) {
-                        // If this theme is requesting a wallpaper, and the wallpaper
-                        // is not currently visible, then this effectively serves as
-                        // an opaque window and our starting window transition animation
-                        // can still work.  We just need to make sure the starting window
-                        // is also showing the wallpaper.
-                        windowFlags |= FLAG_SHOW_WALLPAPER;
-                    } else {
-                        return false;
-                    }
-                }
-            }
-
-            if (mContainer.transferStartingWindow(transferFrom)) {
-                return true;
-            }
-
-            // There is no existing starting window, and we don't want to create a splash screen, so
-            // that's it!
-            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                return false;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
-            mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
-                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                    mContainer.getMergedOverrideConfiguration());
-            scheduleAddStartingWindow();
-        }
-        return true;
-    }
-
-    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
-            TaskSnapshot snapshot) {
-        if (mContainer.getDisplayContent().mAppTransition.getAppTransition()
-                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
-            // TODO(b/34099271): Remove this statement to add back the starting window and figure
-            // out why it causes flickering, the starting window appears over the thumbnail while
-            // the docked from recents transition occurs
-            return STARTING_WINDOW_TYPE_NONE;
-        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
-            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else if (taskSwitch && allowTaskSnapshot) {
-            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
-                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
-                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
-        } else {
-            return STARTING_WINDOW_TYPE_NONE;
-        }
-    }
-
-    void scheduleAddStartingWindow() {
-        // Note: we really want to do sendMessageAtFrontOfQueue() because we
-        // want to process the message ASAP, before any other queued
-        // messages.
-        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
-        }
-    }
-
-    private boolean createSnapshot(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
-        mContainer.startingData = new SnapshotStartingData(mService, snapshot);
-        scheduleAddStartingWindow();
-        return true;
-    }
-
-    private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
-        if (snapshot == null) {
-            return false;
-        }
-        return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
-    }
-
-    public void removeStartingWindow() {
-        synchronized (mGlobalLock) {
-            if (mContainer.startingWindow == null) {
-                if (mContainer.startingData != null) {
-                    // Starting window has not been added yet, but it is scheduled to be added.
-                    // Go ahead and cancel the request.
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                            "Clearing startingData for token=" + mContainer);
-                    mContainer.startingData = null;
-                }
-                return;
-            }
-
-            final StartingSurface surface;
-            if (mContainer.startingData != null) {
-                surface = mContainer.startingSurface;
-                mContainer.startingData = null;
-                mContainer.startingSurface = null;
-                mContainer.startingWindow = null;
-                mContainer.startingDisplayed = false;
-                if (surface == null) {
-                    if (DEBUG_STARTING_WINDOW) {
-                        Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
-                                + "remove");
-                    }
-                    return;
-                }
-            } else {
-                if (DEBUG_STARTING_WINDOW) {
-                    Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
-                            + mContainer);
-                }
-                return;
-            }
-
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
-                    + " startingWindow=" + mContainer.startingWindow
-                    + " startingView=" + mContainer.startingSurface
-                    + " Callers=" + Debug.getCallers(5));
-
-            // Use the same thread to remove the window as we used to add it, as otherwise we end up
-            // with things in the view hierarchy being called from different threads.
-            mService.mAnimationHandler.post(() -> {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
-                try {
-                    surface.remove();
-                } catch (Exception e) {
-                    Slog.w(TAG_WM, "Exception when removing starting window", e);
-                }
-            });
-        }
-    }
-
-    public void pauseKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void resumeKeyDispatching() {
-        synchronized (mGlobalLock) {
-            if (mContainer != null) {
-                mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer);
-            }
-        }
-    }
-
-    public void notifyAppResumed(boolean wasStopped) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken);
-                return;
-            }
-            mContainer.notifyAppResumed(wasStopped);
-        }
-    }
-
-    public void notifyAppStopping() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.detachChildren();
-        }
-    }
-
-    public void notifyAppStopped() {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: "
-                        + mToken);
-                return;
-            }
-            mContainer.notifyAppStopped();
-        }
-    }
-
-    public void startFreezingScreen(int configChanges) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM,
-                        "Attempted to freeze screen with non-existing app token: " + mContainer);
-                return;
-            }
-
-            if (configChanges == 0 && mContainer.okToDisplay()) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken);
-                return;
-            }
-
-            mContainer.startFreezingScreen();
-        }
-    }
-
-    public void stopFreezingScreen(boolean force) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden="
-                    + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen());
-            mContainer.stopFreezingScreen(true, force);
-        }
-    }
-
-    public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app"
-                        + " token: " + mToken);
-                return;
-            }
-            mContainer.registerRemoteAnimations(definition);
-        }
-    }
-
-    void reportStartingWindowDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN));
-    }
-
-    void reportWindowsDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN));
-    }
-
-    void reportWindowsNotDrawn() {
-        mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN));
-    }
-
-    void reportWindowsVisible() {
-        mHandler.post(mOnWindowsVisible);
-    }
-
-    void reportWindowsGone() {
-        mHandler.post(mOnWindowsGone);
-    }
-
-    /** Calls directly into activity manager so window manager lock shouldn't held. */
-    boolean keyDispatchingTimedOut(String reason, int windowPid) {
-        return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid);
-    }
-
-    /**
-     * Apply override app transition base on options & animation type.
-     */
-    public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
-        synchronized (mGlobalLock) {
-            final int animationType = pendingOptions.getAnimationType();
-            final DisplayContent displayContent = mContainer.getDisplayContent();
-            switch (animationType) {
-                case ANIM_CUSTOM:
-                    displayContent.mAppTransition.overridePendingAppTransition(
-                            pendingOptions.getPackageName(),
-                            pendingOptions.getCustomEnterResId(),
-                            pendingOptions.getCustomExitResId(),
-                            pendingOptions.getOnAnimationStartListener());
-                    break;
-                case ANIM_CLIP_REVEAL:
-                    displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_SCALE_UP:
-                    displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getWidth(), pendingOptions.getHeight());
-                    if (intent.getSourceBounds() == null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                pendingOptions.getStartY() + pendingOptions.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_SCALE_UP:
-                case ANIM_THUMBNAIL_SCALE_DOWN:
-                    final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
-                    final GraphicBuffer buffer = pendingOptions.getThumbnail();
-                    displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
-                            pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getOnAnimationStartListener(),
-                            scaleUp);
-                    if (intent.getSourceBounds() == null && buffer != null) {
-                        intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                pendingOptions.getStartY(),
-                                pendingOptions.getStartX() + buffer.getWidth(),
-                                pendingOptions.getStartY() + buffer.getHeight()));
-                    }
-                    break;
-                case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
-                case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
-                    final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
-                    final IAppTransitionAnimationSpecsFuture specsFuture =
-                            pendingOptions.getSpecsFuture();
-                    if (specsFuture != null) {
-                        // TODO(multidisplay): Shouldn't be really used anymore from next CL.
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
-                                specsFuture, pendingOptions.getOnAnimationStartListener(),
-                                animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
-                    } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
-                            && specs != null) {
-                        displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
-                                specs, pendingOptions.getOnAnimationStartListener(),
-                                pendingOptions.getAnimationFinishedListener(), false);
-                    } else {
-                        displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
-                                pendingOptions.getThumbnail(),
-                                pendingOptions.getStartX(), pendingOptions.getStartY(),
-                                pendingOptions.getWidth(), pendingOptions.getHeight(),
-                                pendingOptions.getOnAnimationStartListener(),
-                                (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
-                        if (intent.getSourceBounds() == null) {
-                            intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
-                                    pendingOptions.getStartY(),
-                                    pendingOptions.getStartX() + pendingOptions.getWidth(),
-                                    pendingOptions.getStartY() + pendingOptions.getHeight()));
-                        }
-                    }
-                    break;
-                case ANIM_OPEN_CROSS_PROFILE_APPS:
-                    displayContent.mAppTransition
-                            .overridePendingAppTransitionStartCrossProfileApps();
-                    break;
-                case ANIM_REMOTE_ANIMATION:
-                    // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL.
-                    displayContent.mAppTransition.overridePendingAppTransitionRemote(
-                            pendingOptions.getRemoteAnimationAdapter());
-                    break;
-                case ANIM_NONE:
-                    break;
-                default:
-                    Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP.
-     * This information helps AWT know that the app is in the process of pausing before it gets the
-     * signal on the WM side.
-     */
-    public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) {
-        synchronized (mGlobalLock) {
-            if (mContainer == null) {
-                return;
-            }
-
-            mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "AppWindowContainerController{"
-                + " token=" + mToken
-                + " mContainer=" + mContainer
-                + " mListener=" + mListener
-                + "}";
-    }
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
deleted file mode 100644
index ad27669..0000000
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-/** Interface used by the creator of the controller to listen to changes with the container. */
-public interface AppWindowContainerListener extends WindowContainerListener {
-    /** Called when the windows associated app window container drawn state changes. */
-    void onWindowsDrawn(boolean drawn, long timestamp);
-    /** Called when the windows associated app window container are visible. */
-    void onWindowsVisible();
-    /** Called when the windows associated app window container are no longer visible. */
-    void onWindowsGone();
-
-    /**
-     * Called when the starting window for this container is drawn.
-     */
-    void onStartingWindowDrawn(long timestamp);
-
-    /**
-     * Called when the key dispatching to a window associated with the app window container
-     * timed-out.
-     *
-     * @param reason The reason for the key dispatching time out.
-     * @param windowPid The pid of the window key dispatching timed out on.
-     * @return True if input dispatching should be aborted.
-     */
-    boolean keyDispatchingTimedOut(String reason, int windowPid);
-}
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 729f89b..bb38f30 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -16,13 +16,13 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
+import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
+import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.AppWindowThumbnailProto.HEIGHT;
-import static com.android.server.wm.AppWindowThumbnailProto.SURFACE_ANIMATOR;
-import static com.android.server.wm.AppWindowThumbnailProto.WIDTH;
 
 import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
@@ -53,7 +53,8 @@
 
     AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
         mAppToken = appToken;
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, appToken.mService);
+        mSurfaceAnimator =
+                new SurfaceAnimator(this, this::onAnimationFinished, appToken.mWmService);
         mWidth = thumbnailHeader.getWidth();
         mHeight = thumbnailHeader.getHeight();
 
@@ -65,7 +66,7 @@
         // this to the task.
         mSurfaceControl = appToken.makeSurface()
                 .setName("thumbnail anim: " + appToken.toString())
-                .setSize(mWidth, mHeight)
+                .setBufferSize(mWidth, mHeight)
                 .setFormat(PixelFormat.TRANSLUCENT)
                 .setMetadata(appToken.windowType,
                         window != null ? window.mOwnerUid : Binder.getCallingUid())
@@ -93,11 +94,11 @@
 
     void startAnimation(Transaction t, Animation anim, Point position) {
         anim.restrictDuration(MAX_ANIMATION_DURATION);
-        anim.scaleCurrentDuration(mAppToken.mService.getTransitionAnimationScaleLocked());
+        anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked());
         mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, position,
                         mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()),
-                mAppToken.mService.mSurfaceAnimationRunner), false /* hidden */);
+                mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */);
     }
 
     private void onAnimationFinished() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 52c78ce..c458c94 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -28,10 +28,12 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
 import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 
@@ -81,7 +83,9 @@
 
 import android.annotation.CallSuper;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.content.ComponentName;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
 import android.graphics.Point;
@@ -90,6 +94,7 @@
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -106,6 +111,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.server.AttributeCache;
+import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.WindowManagerService.H;
 
@@ -121,7 +128,8 @@
  * Version of WindowToken that is specifically for a particular application (or
  * really activity) that is displaying windows.
  */
-class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener {
+class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener,
+        ConfigurationContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM;
 
     /**
@@ -226,6 +234,9 @@
 
     private Task mLastParent;
 
+    // TODO: Remove after unification
+    ActivityRecord mActivityRecord;
+
     /**
      * See {@link #canTurnScreenOn()}
      */
@@ -273,14 +284,20 @@
     /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
     boolean mNeedsAnimationBoundsLayer;
 
+    private static final int STARTING_WINDOW_TYPE_NONE = 0;
+    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
     AppWindowToken(WindowManagerService service, IApplicationToken token,
             ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
             long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
             int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
             boolean launchTaskBehind, boolean alwaysFocusable,
-            AppWindowContainerController controller) {
+            ActivityRecord activityRecord) {
         this(service, token, activityComponent, voiceInteraction, dc, fullscreen);
-        setController(controller);
+        // TODO: remove after unification
+        mActivityRecord = activityRecord;
+        mActivityRecord.registerConfigurationChangeListener(this);
         mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
         mShowForAllUsers = showForAllUsers;
         mTargetSdk = targetSdk;
@@ -304,7 +321,7 @@
         mActivityComponent = activityComponent;
         mVoiceInteraction = voiceInteraction;
         mFillsParent = fillsParent;
-        mInputApplicationHandle = new InputApplicationHandle(this);
+        mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
     }
 
     void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
@@ -320,9 +337,7 @@
             // it from behind the starting window, so there is no need for it to also be doing its
             // own stuff.
             win.cancelAnimation();
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
         updateReportedVisibilityLocked();
     }
@@ -360,16 +375,9 @@
         }
         if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
-        final AppWindowContainerController controller = getController();
         if (nowDrawn != reportedDrawn) {
-            if (nowDrawn) {
-                if (controller != null) {
-                    controller.reportWindowsDrawn();
-                }
-            } else {
-                if (controller != null) {
-                    controller.reportWindowsNotDrawn();
-                }
+            if (mActivityRecord != null) {
+                mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
             }
             reportedDrawn = nowDrawn;
         }
@@ -377,16 +385,36 @@
             if (DEBUG_VISIBILITY) Slog.v(TAG,
                     "Visibility changed in " + this + ": vis=" + nowVisible);
             reportedVisible = nowVisible;
-            if (controller != null) {
+            if (mActivityRecord != null) {
                 if (nowVisible) {
-                    controller.reportWindowsVisible();
+                    onWindowsVisible();
                 } else {
-                    controller.reportWindowsGone();
+                    onWindowsGone();
                 }
             }
         }
     }
 
+    private void onWindowsGone() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsGone();
+    }
+
+    private void onWindowsVisible() {
+        if (mActivityRecord == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) {
+            Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken);
+        }
+        mActivityRecord.onWindowsVisible();
+    }
+
     boolean isClientHidden() {
         return mClientHidden;
     }
@@ -401,7 +429,116 @@
         sendAppVisibilityToClients();
     }
 
-    boolean setVisibility(WindowManager.LayoutParams lp,
+    void setVisibility(boolean visible, boolean deferHidingClient) {
+        final AppTransition appTransition = getDisplayContent().mAppTransition;
+
+        // Don't set visibility to false if we were already not visible. This prevents WM from
+        // adding the app to the closing app list which doesn't make sense for something that is
+        // already not visible. However, set visibility to true even if we are already visible.
+        // This makes sure the app is added to the opening apps list so that the right
+        // transition can be selected.
+        // TODO: Probably a good idea to separate the concept of opening/closing apps from the
+        // concept of setting visibility...
+        if (!visible && hiddenRequested) {
+
+            if (!deferHidingClient && mDeferHidingClient) {
+                // We previously deferred telling the client to hide itself when visibility was
+                // initially set to false. Now we would like it to hide, so go ahead and set it.
+                mDeferHidingClient = deferHidingClient;
+                setClientHidden(true);
+            }
+            return;
+        }
+
+        if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) {
+            Slog.v(TAG_WM, "setAppVisibility("
+                    + appToken + ", visible=" + visible + "): " + appTransition
+                    + " hidden=" + isHidden() + " hiddenRequested="
+                    + hiddenRequested + " Callers=" + Debug.getCallers(6));
+        }
+
+        final DisplayContent displayContent = getDisplayContent();
+        displayContent.mOpeningApps.remove(this);
+        displayContent.mClosingApps.remove(this);
+        waitingToShow = false;
+        hiddenRequested = !visible;
+        mDeferHidingClient = deferHidingClient;
+
+        if (!visible) {
+            // If the app is dead while it was visible, we kept its dead window on screen.
+            // Now that the app is going invisible, we can remove it. It will be restarted
+            // if made visible again.
+            removeDeadWindows();
+        } else {
+            if (!appTransition.isTransitionSet()
+                    && appTransition.isReady()) {
+                // Add the app mOpeningApps if transition is unset but ready. This means
+                // we're doing a screen freeze, and the unfreeze will wait for all opening
+                // apps to be ready.
+                displayContent.mOpeningApps.add(this);
+            }
+            startingMoved = false;
+            // If the token is currently hidden (should be the common case), or has been
+            // stopped, then we need to set up to wait for its windows to be ready.
+            if (isHidden() || mAppStopped) {
+                clearAllDrawn();
+
+                // If the app was already visible, don't reset the waitingToShow state.
+                if (isHidden()) {
+                    waitingToShow = true;
+                }
+            }
+
+            // In the case where we are making an app visible but holding off for a transition,
+            // we still need to tell the client to make its windows visible so they get drawn.
+            // Otherwise, we will wait on performing the transition until all windows have been
+            // drawn, they never will be, and we are sad.
+            setClientHidden(false);
+
+            requestUpdateWallpaperIfNeeded();
+
+            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this);
+            mAppStopped = false;
+
+            transferStartingWindowFromHiddenAboveTokenIfNeeded();
+        }
+
+        // If we are preparing an app transition, then delay changing
+        // the visibility of this token until we execute that transition.
+        if (okToAnimate() && appTransition.isTransitionSet()) {
+            inPendingTransaction = true;
+            if (visible) {
+                displayContent.mOpeningApps.add(this);
+                mEnteringAnimation = true;
+            } else {
+                displayContent.mClosingApps.add(this);
+                mEnteringAnimation = false;
+            }
+            if (appTransition.getAppTransition()
+                    == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
+                // We're launchingBehind, add the launching activity to mOpeningApps.
+                final WindowState win = getDisplayContent().findFocusedWindow();
+                if (win != null) {
+                    final AppWindowToken focusedToken = win.mAppToken;
+                    if (focusedToken != null) {
+                        if (DEBUG_APP_TRANSITIONS) {
+                            Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, "
+                                    + " adding " + focusedToken + " to mOpeningApps");
+                        }
+                        // Force animation to be loaded.
+                        focusedToken.setHidden(true);
+                        displayContent.mOpeningApps.add(focusedToken);
+                    }
+                }
+            }
+            return;
+        }
+
+        commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction);
+        updateReportedVisibilityLocked();
+    }
+
+    boolean commitVisibility(WindowManager.LayoutParams lp,
             boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
 
         boolean delayed = false;
@@ -417,7 +554,8 @@
         // * or this is an opening app and windows are being replaced.
         boolean visibilityChanged = false;
         if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) {
-            final AccessibilityController accessibilityController = mService.mAccessibilityController;
+            final AccessibilityController accessibilityController =
+                    mWmService.mAccessibilityController;
             boolean changed = false;
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,
                     "Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout);
@@ -458,18 +596,20 @@
 
                 // We are becoming visible, so better freeze the screen with the windows that are
                 // getting visible so we also wait for them.
-                forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true);
+                forAllWindows(mWmService::makeWindowFreezingScreenIfNeededLocked, true);
             }
 
-            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this
-                    + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            if (DEBUG_APP_TRANSITIONS) {
+                Slog.v(TAG_WM, "commitVisibility: " + this
+                        + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested);
+            }
 
             if (changed) {
                 getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw();
                 if (performLayout) {
-                    mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+                    mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                             false /*updateInputWindows*/);
-                    mService.mWindowPlacerLocked.performSurfacePlacement();
+                    mWmService.mWindowPlacerLocked.performSurfacePlacement();
                 }
                 getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
             }
@@ -496,13 +636,13 @@
                 // The token was made immediately visible, there will be no entrance animation.
                 // We need to inform the client the enter animation was finished.
                 mEnteringAnimation = true;
-                mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
+                mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+                        token);
             }
 
-            // If we're becoming visible, immediately change client visibility as well although it
-            // usually gets changed in AppWindowContainerController.setVisibility already. However,
-            // there seem to be some edge cases where we change our visibility but client visibility
-            // never gets updated.
+            // If we're becoming visible, immediately change client visibility as well. there seem
+            // to be some edge cases where we change our visibility but client visibility never gets
+            // updated.
             // If we're becoming invisible, update the client visibility if we are not running an
             // animation. Otherwise, we'll update client visibility in onAnimationFinished.
             if (visible || !isReallyAnimating()) {
@@ -520,7 +660,7 @@
 
                 // Take the screenshot before possibly hiding the WSA, otherwise the screenshot
                 // will not be taken.
-                mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
+                mWmService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible);
             }
 
             // If we are hidden but there is no delay needed we immediately
@@ -596,11 +736,6 @@
         return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable;
     }
 
-    AppWindowContainerController getController() {
-        final WindowContainerController controller = super.getController();
-        return controller != null ? (AppWindowContainerController) controller : null;
-    }
-
     @Override
     boolean isVisible() {
         // If the app token isn't hidden then it is considered visible and there is no need to check
@@ -637,11 +772,11 @@
 
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this);
 
-        boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
+        boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction);
 
         getDisplayContent().mOpeningApps.remove(this);
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
-        mService.mTaskSnapshotController.onAppRemoved(this);
+        mWmService.mTaskSnapshotController.onAppRemoved(this);
         waitingToShow = false;
         if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
@@ -656,8 +791,8 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
-        if (startingData != null && getController() != null) {
-            getController().removeStartingWindow();
+        if (startingData != null) {
+            removeStartingWindow();
         }
 
         // If this window was animating, then we need to ensure that the app transition notifies
@@ -694,7 +829,7 @@
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
                    + " displayId=" + dc.getDisplayId());
             dc.setFocusedApp(null);
-            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
         }
 
         if (!delayed) {
@@ -768,9 +903,7 @@
         mAppStopped = true;
         destroySurfaces();
         // Remove any starting window that was added for this app if they are still around.
-        if (getController() != null) {
-            getController().removeStartingWindow();
-        }
+        removeStartingWindow();
     }
 
     void clearAllDrawn() {
@@ -826,9 +959,7 @@
         // TODO: Something smells about the code below...Is there a better way?
         if (startingWindow == win) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         } else if (mChildren.size() == 0) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
@@ -845,9 +976,7 @@
             // we need to get rid of the starting transition.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window "
                     + win);
-            if (getController() != null) {
-                getController().removeStartingWindow();
-            }
+            removeStartingWindow();
         }
     }
 
@@ -1014,13 +1143,17 @@
 
         // if we got a replacement window, reset the timeout to give drawing more time
         if (gotReplacementWindow) {
-            mService.scheduleWindowReplacementTimeouts(this);
+            mWmService.scheduleWindowReplacementTimeouts(this);
         }
         checkKeyguardFlagsChanged();
     }
 
     @Override
     void removeChild(WindowState child) {
+        if (!mChildren.contains(child)) {
+            // This can be true when testing.
+            return;
+        }
         super.removeChild(child);
         checkKeyguardFlagsChanged();
         updateLetterboxSurface(child);
@@ -1042,6 +1175,20 @@
         }
     }
 
+    void reparent(TaskWindowContainerController taskController, int position) {
+        if (DEBUG_ADD_REMOVE) {
+            Slog.i(TAG_WM, "reparent: moving app token=" + this
+                    + " to task=" + taskController + " at " + position);
+        }
+        final Task task = taskController.mContainer;
+        if (task == null) {
+            throw new IllegalArgumentException("reparent: could not find task="
+                    + taskController);
+        }
+        reparent(task, position);
+        getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
+    }
+
     void reparent(Task task, int position) {
         final Task currentTask = getTask();
         if (task == currentTask) {
@@ -1126,7 +1273,7 @@
             final WindowState win = mChildren.get(i);
             win.onUnfreezeBounds();
         }
-        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
     void setAppLayoutChanges(int changes, String reason) {
@@ -1134,7 +1281,7 @@
             final DisplayContent dc = getDisplayContent();
             dc.pendingLayoutChanges |= changes;
             if (DEBUG_LAYOUT_REPEATS) {
-                mService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
+                mWmService.mWindowPlacerLocked.debugLayoutRepeats(reason, dc.pendingLayoutChanges);
             }
         }
     }
@@ -1155,12 +1302,12 @@
         if (!hiddenRequested) {
             if (!mFreezingScreen) {
                 mFreezingScreen = true;
-                mService.registerAppFreezeListener(this);
-                mService.mAppsFreezingScreen++;
-                if (mService.mAppsFreezingScreen == 1) {
-                    mService.startFreezingDisplayLocked(0, 0, getDisplayContent());
-                    mService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
-                    mService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
+                mWmService.registerAppFreezeListener(this);
+                mWmService.mAppsFreezingScreen++;
+                if (mWmService.mAppsFreezingScreen == 1) {
+                    mWmService.startFreezingDisplayLocked(0, 0, getDisplayContent());
+                    mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
+                    mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
                 }
             }
             final int count = mChildren.size();
@@ -1185,15 +1332,15 @@
         if (force || unfrozeWindows) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "No longer freezing: " + this);
             mFreezingScreen = false;
-            mService.unregisterAppFreezeListener(this);
-            mService.mAppsFreezingScreen--;
-            mService.mLastFinishedFreezeSource = this;
+            mWmService.unregisterAppFreezeListener(this);
+            mWmService.mAppsFreezingScreen--;
+            mWmService.mLastFinishedFreezeSource = this;
         }
         if (unfreezeSurfaceNow) {
             if (unfrozeWindows) {
-                mService.mWindowPlacerLocked.performSurfacePlacement();
+                mWmService.mWindowPlacerLocked.performSurfacePlacement();
             }
-            mService.stopFreezingDisplayLocked();
+            mWmService.stopFreezingDisplayLocked();
         }
     }
 
@@ -1284,10 +1431,10 @@
                 // pending opening apps.
                 getDisplayContent().mOpeningApps.remove(this);
 
-                mService.updateFocusedWindowLocked(
+                mWmService.updateFocusedWindowLocked(
                         UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
                 getDisplayContent().setLayoutNeeded();
-                mService.mWindowPlacerLocked.performSurfacePlacement();
+                mWmService.mWindowPlacerLocked.performSurfacePlacement();
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -1300,9 +1447,7 @@
             startingData = fromToken.startingData;
             fromToken.startingData = null;
             fromToken.startingMoved = true;
-            if (getController() != null) {
-                getController().scheduleAddStartingWindow();
-            }
+            scheduleAddStartingWindow();
             return true;
         }
 
@@ -1459,7 +1604,7 @@
                 if (mDisplayContent != null) {
                     mDisplayContent.setLayoutNeeded();
                 }
-                mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
+                mWmService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
 
                 // Notify the pinned stack upon all windows drawn. If there was an animation in
                 // progress then this signal will resume that animation.
@@ -1471,6 +1616,10 @@
         }
     }
 
+    boolean keyDispatchingTimedOut(String reason, int windowPid) {
+        return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid);
+    }
+
     /**
      * Updated this app token tracking states for interesting and drawn windows based on the window.
      *
@@ -1489,8 +1638,8 @@
             return false;
         }
 
-        if (mLastTransactionSequence != mService.mTransactionSequence) {
-            mLastTransactionSequence = mService.mTransactionSequence;
+        if (mLastTransactionSequence != mWmService.mTransactionSequence) {
+            mLastTransactionSequence = mWmService.mTransactionSequence;
             mNumDrawnWindows = 0;
             startingDisplayed = false;
 
@@ -1533,8 +1682,8 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                if (getController() != null) {
-                    getController().reportStartingWindowDrawn();
+                if (mActivityRecord != null) {
+                    mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis());
                 }
                 startingDisplayed = true;
             }
@@ -1601,6 +1750,266 @@
         return this;
     }
 
+    boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
+            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
+        // If the display is frozen, we won't do anything until the actual window is
+        // displayed so there is no reason to put in the starting window.
+        if (!okToDisplay()) {
+            return false;
+        }
+
+        if (startingData != null) {
+            return false;
+        }
+
+        final WindowState mainWin = findMainWindow();
+        if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+            // App already has a visible window...why would you want a starting window?
+            return false;
+        }
+
+        final ActivityManager.TaskSnapshot snapshot =
+                mWmService.mTaskSnapshotController.getSnapshot(
+                        getTask().mTaskId, getTask().mUserId,
+                        false /* restoreFromDisk */, false /* reducedResolution */);
+        final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
+                allowTaskSnapshot, activityCreated, fromRecents, snapshot);
+
+        if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+            return createSnapshot(snapshot);
+        }
+
+        // If this is a translucent window, then don't show a starting window -- the current
+        // effect (a full-screen opaque starting window that fades away to the real contents
+        // when it is ready) does not work for this.
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme));
+        }
+        if (theme != 0) {
+            AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+                    com.android.internal.R.styleable.Window,
+                    mWmService.mCurrentUserId);
+            if (ent == null) {
+                // Whoops!  App doesn't exist. Um. Okay. We'll just pretend like we didn't
+                // see that.
+                return false;
+            }
+            final boolean windowIsTranslucent = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsTranslucent, false);
+            final boolean windowIsFloating = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowIsFloating, false);
+            final boolean windowShowWallpaper = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowShowWallpaper, false);
+            final boolean windowDisableStarting = ent.array.getBoolean(
+                    com.android.internal.R.styleable.Window_windowDisablePreview, false);
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Translucent=" + windowIsTranslucent
+                        + " Floating=" + windowIsFloating
+                        + " ShowWallpaper=" + windowShowWallpaper);
+            }
+            if (windowIsTranslucent) {
+                return false;
+            }
+            if (windowIsFloating || windowDisableStarting) {
+                return false;
+            }
+            if (windowShowWallpaper) {
+                if (getDisplayContent().mWallpaperController
+                        .getWallpaperTarget() == null) {
+                    // If this theme is requesting a wallpaper, and the wallpaper
+                    // is not currently visible, then this effectively serves as
+                    // an opaque window and our starting window transition animation
+                    // can still work.  We just need to make sure the starting window
+                    // is also showing the wallpaper.
+                    windowFlags |= FLAG_SHOW_WALLPAPER;
+                } else {
+                    return false;
+                }
+            }
+
+            if (transferStartingWindow(transferFrom)) {
+                return true;
+            }
+
+            // There is no existing starting window, and we don't want to create a splash screen, so
+            // that's it!
+            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
+                return false;
+            }
+
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData");
+            startingData = new SplashScreenStartingData(mWmService, pkg,
+                    theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                    getMergedOverrideConfiguration());
+            scheduleAddStartingWindow();
+        }
+        return true;
+    }
+
+
+    private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+
+        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData");
+        startingData = new SnapshotStartingData(mWmService, snapshot);
+        scheduleAddStartingWindow();
+        return true;
+    }
+
+    void scheduleAddStartingWindow() {
+        // Note: we really want to do sendMessageAtFrontOfQueue() because we
+        // want to process the message ASAP, before any other queued
+        // messages.
+        if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING");
+            mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+        }
+    }
+
+    private final Runnable mAddStartingWindow = new Runnable() {
+
+        @Override
+        public void run() {
+            synchronized (mWmService.mGlobalLock) {
+                // There can only be one adding request, silly caller!
+                mWmService.mAnimationHandler.removeCallbacks(this);
+            }
+
+            if (startingData == null) {
+                // Animation has been canceled... do nothing.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG, "startingData was nulled out before handling"
+                            + " mAddStartingWindow: " + AppWindowToken.this);
+                }
+                return;
+            }
+
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData);
+            }
+
+            WindowManagerPolicy.StartingSurface surface = null;
+            try {
+                surface = startingData.createStartingSurface(AppWindowToken.this);
+            } catch (Exception e) {
+                Slog.w(TAG, "Exception when adding starting window", e);
+            }
+            if (surface != null) {
+                boolean abort = false;
+                synchronized (mWmService.mGlobalLock) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (removed || startingData == null) {
+                        if (DEBUG_STARTING_WINDOW) {
+                            Slog.v(TAG, "Aborted starting " + AppWindowToken.this
+                                    + ": removed=" + removed + " startingData=" + startingData);
+                        }
+                        startingWindow = null;
+                        startingData = null;
+                        abort = true;
+                    } else {
+                        startingSurface = surface;
+                    }
+                    if (DEBUG_STARTING_WINDOW && !abort) {
+                        Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow="
+                                + startingWindow + " startingView=" + startingSurface);
+                    }
+                }
+                if (abort) {
+                    surface.remove();
+                }
+            } else if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this);
+            }
+        }
+    };
+
+    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
+            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
+            ActivityManager.TaskSnapshot snapshot) {
+        if (getDisplayContent().mAppTransition.getAppTransition()
+                == TRANSIT_DOCK_TASK_FROM_RECENTS) {
+            // TODO(b/34099271): Remove this statement to add back the starting window and figure
+            // out why it causes flickering, the starting window appears over the thumbnail while
+            // the docked from recents transition occurs
+            return STARTING_WINDOW_TYPE_NONE;
+        } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else if (taskSwitch && allowTaskSnapshot) {
+            return snapshot == null ? STARTING_WINDOW_TYPE_NONE
+                    : snapshotOrientationSameAsTask(snapshot) || fromRecents
+                            ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else {
+            return STARTING_WINDOW_TYPE_NONE;
+        }
+    }
+
+
+    private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) {
+        if (snapshot == null) {
+            return false;
+        }
+        return getTask().getConfiguration().orientation == snapshot.getOrientation();
+    }
+
+    void removeStartingWindow() {
+        if (startingWindow == null) {
+            if (startingData != null) {
+                // Starting window has not been added yet, but it is scheduled to be added.
+                // Go ahead and cancel the request.
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "Clearing startingData for token=" + this);
+                }
+                startingData = null;
+            }
+            return;
+        }
+
+        final WindowManagerPolicy.StartingSurface surface;
+        if (startingData != null) {
+            surface = startingSurface;
+            startingData = null;
+            startingSurface = null;
+            startingWindow = null;
+            startingDisplayed = false;
+            if (surface == null) {
+                if (DEBUG_STARTING_WINDOW) {
+                    Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't "
+                            + "remove");
+                }
+                return;
+            }
+        } else {
+            if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:"
+                        + this);
+            }
+            return;
+        }
+
+        if (DEBUG_STARTING_WINDOW) {
+            Slog.v(TAG_WM, "Schedule remove starting " + this
+                    + " startingWindow=" + startingWindow
+                    + " startingView=" + startingSurface
+                    + " Callers=" + Debug.getCallers(5));
+        }
+
+        // Use the same thread to remove the window as we used to add it, as otherwise we end up
+        // with things in the view hierarchy being called from different threads.
+        mWmService.mAnimationHandler.post(() -> {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface);
+            try {
+                surface.remove();
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        });
+    }
+
     @Override
     boolean fillsParent() {
         return mFillsParent;
@@ -1647,7 +2056,7 @@
         final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
         if (containsDismissKeyguard != mLastContainsDismissKeyguardWindow
                 || containsShowWhenLocked != mLastContainsShowWhenLockedWindow) {
-            mService.notifyKeyguardFlagsChanged(null /* callback */,
+            mWmService.notifyKeyguardFlagsChanged(null /* callback */,
                     getDisplayContent().getDisplayId());
         }
         mLastContainsDismissKeyguardWindow = containsDismissKeyguard;
@@ -1754,10 +2163,8 @@
         if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
         final SurfaceControl.Builder builder = makeAnimationLeash()
                 .setParent(getAnimationLeashParent())
-                .setName(getSurfaceControl() + " - animation-bounds")
-                .setSize(getSurfaceWidth(), getSurfaceHeight());
+                .setName(getSurfaceControl() + " - animation-bounds");
         final SurfaceControl boundsLayer = builder.build();
-        t.setWindowCrop(boundsLayer, getSurfaceWidth(), getSurfaceHeight());
         t.show(boundsLayer);
         return boundsLayer;
     }
@@ -1771,9 +2178,9 @@
         final TaskStack stack = getStack();
         final Task task = getTask();
         if (task != null && task.inFreeformWindowingMode()) {
-            task.getRelativePosition(outPosition);
+            task.getRelativeDisplayedPosition(outPosition);
         } else if (stack != null) {
-            stack.getRelativePosition(outPosition);
+            stack.getRelativeDisplayedPosition(outPosition);
         }
 
         // Always use stack bounds in order to have the ability to animate outside the task region.
@@ -1786,10 +2193,22 @@
         outBounds.offsetTo(0, 0);
     }
 
+    @Override
+    Rect getDisplayedBounds() {
+        final Task task = getTask();
+        if (task != null) {
+            final Rect overrideDisplayedBounds = task.getOverrideDisplayedBounds();
+            if (!overrideDisplayedBounds.isEmpty()) {
+                return overrideDisplayedBounds;
+            }
+        }
+        return getBounds();
+    }
+
     boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
             boolean isVoiceInteraction) {
 
-        if (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
+        if (mWmService.mDisableTransitionAnimation || !shouldAnimate(transit)) {
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped."
                         + " atoken=" + this);
@@ -1823,7 +2242,7 @@
                                     getDisplayContent().mAppTransition.canSkipFirstFrame(),
                                     appStackClipMode,
                                     true /* isAppAnimation */),
-                            mService.mSurfaceAnimationRunner);
+                            mWmService.mSurfaceAnimationRunner);
                     if (a.getZAdjustment() == Animation.ZORDER_TOP) {
                         mNeedsZBoost = true;
                     }
@@ -1906,7 +2325,7 @@
             final int containingWidth = frame.width();
             final int containingHeight = frame.height();
             a.initialize(containingWidth, containingHeight, width, height);
-            a.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+            a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
         }
         return a;
     }
@@ -2111,7 +2530,7 @@
             return;
         }
         final Rect frame = win.getFrameLw();
-        final int thumbnailDrawableRes = getTask().mUserId == mService.mCurrentUserId
+        final int thumbnailDrawableRes = getTask().mUserId == mWmService.mCurrentUserId
                 ? R.drawable.ic_account_circle
                 : R.drawable.ic_corp_badge;
         final GraphicBuffer thumbnail =
@@ -2215,9 +2634,6 @@
         if (mPendingRelaunchCount != 0) {
             pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
         }
-        if (getController() != null) {
-            pw.print(prefix); pw.print("controller="); pw.println(getController());
-        }
         if (mRemovingFromDisplay) {
             pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
         }
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 9633864..c90f5bf 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -48,7 +48,6 @@
 
             surface = dc.makeOverlay()
                     .setName("BlackSurface")
-                    .setSize(w, h)
                     .setColorLayer(true)
                     .setParent(null) // TODO: Work-around for b/69259549
                     .build();
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 2a216ab..c3d6211 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -69,7 +69,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("CircularDisplayMask")
-                    .setSize(mScreenSize.x, mScreenSize.y) // not a typo
+                    .setBufferSize(mScreenSize.x, mScreenSize.y) // not a typo
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index cc14afc..aea071f 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -131,7 +131,7 @@
                 if (!mDimming) {
                     dimAnimatable.getPendingTransaction().destroy(mDimLayer);
                 }
-            }, mHost.mService);
+            }, mHost.mWmService);
         }
     }
 
@@ -308,7 +308,6 @@
             return false;
         } else {
             // TODO: Once we use geometry from hierarchy this falls away.
-            t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
             t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
             t.setWindowCrop(mDimState.mDimLayer, bounds.width(), bounds.height());
             if (!mDimState.isVisible) {
@@ -334,7 +333,7 @@
             SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
         mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
                 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
-                mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
+                mHost.mWmService.mSurfaceAnimationRunner), false /* hidden */);
     }
 
     private long getDimDuration(WindowContainer container) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 886b2ff..05e8267 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -29,6 +29,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
+import static android.view.InsetsState.TYPE_IME;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
@@ -78,7 +79,6 @@
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.STACKS;
-import static com.android.server.wm.DisplayContentProto.SURFACE_SIZE;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
@@ -100,6 +100,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
@@ -124,6 +125,7 @@
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -151,6 +153,7 @@
 import android.view.Gravity;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.InsetsState.InternalInsetType;
 import android.view.MagnificationSpec;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -162,6 +165,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.TriConsumer;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
@@ -205,21 +209,21 @@
 
     /** The containers below are the only child containers the display can have. */
     // Contains all window containers that are related to apps (Activities)
-    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mService);
+    private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
     // Contains all non-app window containers that should be displayed above the app containers
     // (e.g. Status bar)
     private final AboveAppWindowContainers mAboveAppWindowsContainers =
-            new AboveAppWindowContainers("mAboveAppWindowsContainers", mService);
+            new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
     // Contains all non-app window containers that should be displayed below the app containers
     // (e.g. Wallpaper).
     private final NonAppWindowContainers mBelowAppWindowsContainers =
-            new NonAppWindowContainers("mBelowAppWindowsContainers", mService);
+            new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed. We use a subclass of
     // WindowContainer which is omitted from screen magnification, as the IME is never magnified.
     private final NonMagnifiableWindowContainers mImeWindowsContainers =
-            new NonMagnifiableWindowContainers("mImeWindowsContainers", mService);
+            new NonMagnifiableWindowContainers("mImeWindowsContainers", mWmService);
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
@@ -352,6 +356,14 @@
     int pendingLayoutChanges;
     int mDeferredRotationPauseCount;
 
+    /**
+     * Used to gate application window layout until we have sent the complete configuration.
+     * TODO: There are still scenarios where we may be out of sync with the client. Ideally
+     *       we want to replace this flag with a mechanism that will confirm the configuration
+     *       applied by the client is the one expected by the system server.
+     */
+    boolean mWaitingForConfig;
+
     // TODO(multi-display): remove some of the usages.
     @VisibleForTesting
     boolean isDefaultDisplay;
@@ -462,14 +474,6 @@
     private SurfaceControl mWindowingLayer;
 
     /**
-     * Specifies the size of the surfaces in {@link #mOverlayLayer} and {@link #mWindowingLayer}.
-     * <p>
-     * For these surfaces currently we use a surface based on the larger of width or height so we
-     * don't have to resize when rotating the display.
-     */
-    private int mSurfaceSize;
-
-    /**
      * Sequence number for the current layout pass.
      */
     int mLayoutSeq = 0;
@@ -507,6 +511,8 @@
 
     private final PointerEventDispatcher mPointerEventDispatcher;
 
+    private final InsetsStateController mInsetsStateController;
+
     // Last systemUiVisibility we received from status bar.
     private int mLastStatusBarVisibility = 0;
     // Last systemUiVisibility we dispatched to windows.
@@ -520,7 +526,7 @@
                 if (w.performShowLocked()) {
                     pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
                     if (DEBUG_LAYOUT_REPEATS) {
-                        mService.mWindowPlacerLocked.debugLayoutRepeats(
+                        mWmService.mWindowPlacerLocked.debugLayoutRepeats(
                                 "updateWindowsAndWallpaperLocked 5", pendingLayoutChanges);
                     }
                 }
@@ -551,7 +557,7 @@
 
     private final Consumer<WindowState> mScheduleToastTimeout = w -> {
         final int lostFocusUid = mTmpWindow.mOwnerUid;
-        final Handler handler = mService.mH;
+        final Handler handler = mWmService.mH;
         if (w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == lostFocusUid) {
             if (!handler.hasMessages(WINDOW_HIDE_TIMEOUT, w)) {
                 handler.sendMessageDelayed(handler.obtainMessage(WINDOW_HIDE_TIMEOUT, w),
@@ -613,7 +619,7 @@
     private final Consumer<WindowState> mPerformLayout = w -> {
         // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
         // wasting time and funky changes while a window is animating away.
-        final boolean gone = (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w))
+        final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
                 || w.isGoneForLayoutLw();
 
         if (DEBUG_LAYOUT && !w.mLayoutAttached) {
@@ -681,7 +687,7 @@
             // If this view is GONE, then skip it -- keep the current frame, and let the caller
             // know so they can ignore it if they want.  (We do the normal layout for INVISIBLE
             // windows, since that means "perform layout as normal, just don't display").
-            if (mTmpWindow != null && mService.mPolicy.canBeHiddenByKeyguardLw(w)) {
+            if (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w)) {
                 return;
             }
             if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame
@@ -716,10 +722,10 @@
                     mInputMethodTarget);
 
     private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
-        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
         final boolean obscuredChanged = w.mObscured !=
                 mTmpApplySurfaceChangesTransactionState.obscured;
-        final RootWindowContainer root = mService.mRoot;
+        final RootWindowContainer root = mWmService.mRoot;
 
         // Update effect.
         w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -810,7 +816,7 @@
         }
 
         if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
-            mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
+            mWmService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
         }
 
         w.updateResizingWindowIfNeeded();
@@ -835,7 +841,7 @@
 
         mDisplay = display;
         mDisplayId = display.getDisplayId();
-        mWallpaperController = new WallpaperController(mService, this);
+        mWallpaperController = new WallpaperController(mWmService, this);
         display.getDisplayInfo(mDisplayInfo);
         display.getMetrics(mDisplayMetrics);
         isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
@@ -852,8 +858,8 @@
         mBoundsAnimationController = new BoundsAnimationController(service.mContext,
                 mAppTransition, SurfaceAnimationThread.getHandler(), animationHandler);
 
-        if (mService.mInputManager != null) {
-            final InputChannel inputChannel = mService.mInputManager.monitorInput("Display "
+        if (mWmService.mInputManager != null) {
+            final InputChannel inputChannel = mWmService.mInputManager.monitorInput("Display "
                     + mDisplayId, mDisplayId);
             mPointerEventDispatcher = inputChannel != null
                     ? new PointerEventDispatcher(inputChannel) : null;
@@ -865,29 +871,18 @@
         if (isDefaultDisplay) {
             // The policy may be invoked right after here, so it requires the necessary default
             // fields of this display content.
-            mService.mPolicy.setDefaultDisplay(this);
+            mWmService.mPolicy.setDefaultDisplay(this);
         }
-        if (mService.mDisplayReady) {
+        if (mWmService.mDisplayReady) {
             mDisplayPolicy.onConfigurationChanged();
         }
-        if (mService.mSystemReady) {
+        if (mWmService.mSystemReady) {
             mDisplayPolicy.systemReady();
         }
         mDividerControllerLocked = new DockedStackDividerController(service, this);
         mPinnedStackControllerLocked = new PinnedStackController(service, this);
 
-        // We use this as our arbitrary surface size for buffer-less parents
-        // that don't impose cropping on their children. It may need to be larger
-        // than the display size because fullscreen windows can be shifted offscreen
-        // due to surfaceInsets. 2 times the largest display dimension feels like an
-        // appropriately arbitrary number. Eventually we would like to give SurfaceFlinger
-        // layers the ability to match their parent sizes and be able to skip
-        // such arbitrary size settings.
-        mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth) * 2;
-
-        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(mSession)
-                .setSize(mSurfaceSize, mSurfaceSize)
-                .setOpaque(true);
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true);
         mWindowingLayer = b.setName("Display Root").build();
         mOverlayLayer = b.setName("Display Overlays").build();
 
@@ -906,19 +901,20 @@
         super.addChild(mImeWindowsContainers, null);
 
         // Add itself as a child to the root container.
-        mService.mRoot.addChild(this, null);
+        mWmService.mRoot.addChild(this, null);
 
         // TODO(b/62541591): evaluate whether this is the best spot to declare the
         // {@link DisplayContent} ready for use.
         mDisplayReady = true;
 
-        mService.mAnimator.addDisplayLocked(mDisplayId);
+        mWmService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
+        mInsetsStateController = new InsetsStateController(this);
     }
 
     boolean isReady() {
         // The display is ready when the system and the individual display are both ready.
-        return mService.mDisplayReady && mDisplayReady;
+        return mWmService.mDisplayReady && mDisplayReady;
     }
 
     int getDisplayId() {
@@ -938,7 +934,7 @@
     }
 
     private void addWindowToken(IBinder binder, WindowToken token) {
-        final DisplayContent dc = mService.mRoot.getWindowTokenDisplay(token);
+        final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
         if (dc != null) {
             // We currently don't support adding a window token to the display if the display
             // already has the binder mapped to another token. If there is a use case for supporting
@@ -1050,6 +1046,23 @@
         return mDisplayRotation;
     }
 
+    /**
+     * Marks a window as providing insets for the rest of the windows in the system.
+     *
+     * @param type The type of inset this window provides.
+     * @param win The window.
+     * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of
+     *                      the window should be taken.
+     */
+    void setInsetProvider(@InternalInsetType int type, WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        mInsetsStateController.getSourceProvider(type).setWindow(win, frameProvider);
+    }
+
+    InsetsStateController getInsetsStateController() {
+        return mInsetsStateController;
+    }
+
     @VisibleForTesting
     void setDisplayRotation(DisplayRotation displayRotation) {
         mDisplayRotation = displayRotation;
@@ -1127,7 +1140,7 @@
 
     /** Notify the configuration change of this display. */
     void sendNewConfiguration() {
-        mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
+        mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget();
     }
 
     /**
@@ -1202,7 +1215,7 @@
             }
 
             screenRotationAnimation =
-                    mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+                    mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
             if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                 // Rotation updates cannot be performed while the previous rotation change
                 // animation is still in progress.  Skip this update.  We will try updating
@@ -1210,7 +1223,7 @@
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
                 return false;
             }
-            if (mService.mDisplayFrozen) {
+            if (mWmService.mDisplayFrozen) {
                 // Even if the screen rotation animation has finished (e.g. isAnimating
                 // returns false), there is still some time where we haven't yet unfrozen
                 // the display. We also need to abort rotation here.
@@ -1220,7 +1233,7 @@
             }
         }
 
-        if (!mService.mDisplayEnabled) {
+        if (!mWmService.mDisplayEnabled) {
             // No point choosing a rotation if the display is not enabled.
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
             return false;
@@ -1251,8 +1264,8 @@
             if (hasPinnedStack()) {
                 mayRotateSeamlessly = false;
             }
-            for (int i = 0; i < mService.mSessions.size(); i++) {
-                if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
+            for (int i = 0; i < mWmService.mSessions.size(); i++) {
+                if (mWmService.mSessions.valueAt(i).hasAlertWindowSurfaces()) {
                     mayRotateSeamlessly = false;
                     break;
                 }
@@ -1284,14 +1297,14 @@
                 + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
 
         if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
-            mService.mWaitingForConfig = true;
+            mWaitingForConfig = true;
         }
 
         mRotation = rotation;
         mAltOrientation = altOrientation;
 
-        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
-        mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+        mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
+        mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
                 this, WINDOW_FREEZE_TIMEOUT_DURATION);
 
         setLayoutNeeded();
@@ -1299,14 +1312,14 @@
         mDisplayPolicy.selectRotationAnimationLw(anim);
 
         if (!rotateSeamlessly) {
-            mService.startFreezingDisplayLocked(anim[0], anim[1], this);
+            mWmService.startFreezingDisplayLocked(anim[0], anim[1], this);
             // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
         } else {
             // The screen rotation animation uses a screenshot to freeze the screen
             // while windows resize underneath.
             // When we are rotating seamlessly, we allow the elements to transition
             // to their rotated state independently and without a freeze required.
-            mService.startSeamlessRotation();
+            mWmService.startSeamlessRotation();
         }
 
         return true;
@@ -1321,9 +1334,9 @@
      */
     void applyRotationLocked(final int oldRotation, final int rotation) {
         mDisplayRotation.setRotation(rotation);
-        final boolean rotateSeamlessly = mService.isRotatingSeamlessly();
+        final boolean rotateSeamlessly = mWmService.isRotatingSeamlessly();
         ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
-                ? null : mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+                ? null : mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
         // We need to update our screen size information to match the new rotation. If the rotation
         // has actually changed then this method will return true and, according to the comment at
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
@@ -1336,9 +1349,9 @@
         if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
                 && screenRotationAnimation.hasScreenshot()) {
             if (screenRotationAnimation.setRotation(getPendingTransaction(), rotation,
-                    MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
+                    MAX_ANIMATION_DURATION, mWmService.getTransitionAnimationScaleLocked(),
                     mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
-                mService.scheduleAnimationLocked();
+                mWmService.scheduleAnimationLocked();
             }
         }
 
@@ -1347,27 +1360,27 @@
                     rotateSeamlessly);
         }, true /* traverseTopToBottom */);
 
-        mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
+        mWmService.mDisplayManagerInternal.performTraversal(getPendingTransaction());
         scheduleAnimation();
 
         forAllWindows(w -> {
             if (w.mHasSurface && !rotateSeamlessly) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
                 w.setOrientationChanging(true);
-                mService.mRoot.mOrientationChangeComplete = false;
+                mWmService.mRoot.mOrientationChangeComplete = false;
                 w.mLastFreezeDuration = 0;
             }
             w.mReportOrientationChanged = true;
         }, true /* traverseTopToBottom */);
 
         if (rotateSeamlessly) {
-            mService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+            mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
                     this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
         }
 
-        for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
+        for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
             final WindowManagerService.RotationWatcher rotationWatcher
-                    = mService.mRotationWatchers.get(i);
+                    = mWmService.mRotationWatchers.get(i);
             if (rotationWatcher.mDisplayId == mDisplayId) {
                 try {
                     rotationWatcher.mWatcher.onRotationChanged(rotation);
@@ -1380,9 +1393,9 @@
         // TODO (multi-display): Magnification is supported only for the default display.
         // Announce rotation only if we will not animate as we already have the
         // windows in final state. Otherwise, we make this call at the rotation end.
-        if (screenRotationAnimation == null && mService.mAccessibilityController != null
+        if (screenRotationAnimation == null && mWmService.mAccessibilityController != null
                 && isDefaultDisplay) {
-            mService.mAccessibilityController.onRotationChangedLocked(this);
+            mWmService.mAccessibilityController.onRotationChangedLocked(this);
         }
     }
 
@@ -1416,9 +1429,9 @@
                 Slog.d(TAG,
                         "Registering PointerEventListener for DisplayId: " + mDisplayId);
             }
-            mTapDetector = new TaskTapPointerEventListener(mService, this);
+            mTapDetector = new TaskTapPointerEventListener(mWmService, this);
             registerPointerEventListener(mTapDetector);
-            registerPointerEventListener(mService.mMousePositionTracker);
+            registerPointerEventListener(mWmService.mMousePositionTracker);
         }
     }
 
@@ -1483,7 +1496,7 @@
         // because we don't want letter-/pillar-boxing during resize.
         final DisplayInfo overrideDisplayInfo = mShouldOverrideDisplayConfiguration
                 ? mDisplayInfo : null;
-        mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
+        mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
                 overrideDisplayInfo);
 
         mBaseDisplayRect.set(0, 0, dw, dh);
@@ -1569,10 +1582,10 @@
         config.densityDpi = displayInfo.logicalDensityDpi;
 
         config.colorMode =
-                ((displayInfo.isHdr() && mService.hasHdrSupport())
+                ((displayInfo.isHdr() && mWmService.hasHdrSupport())
                         ? Configuration.COLOR_MODE_HDR_YES
                         : Configuration.COLOR_MODE_HDR_NO)
-                        | (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
+                        | (displayInfo.isWideColorGamut() && mWmService.hasWideColorGamutSupport()
                         ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
                         : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
 
@@ -1584,7 +1597,7 @@
 
         int keyboardPresence = 0;
         int navigationPresence = 0;
-        final InputDevice[] devices = mService.mInputManager.getInputDevices();
+        final InputDevice[] devices = mWmService.mInputManager.getInputDevices();
         final int len = devices != null ? devices.length : 0;
         for (int i = 0; i < len; i++) {
             InputDevice device = devices[i];
@@ -1595,7 +1608,7 @@
                         WindowManagerPolicy.PRESENCE_INTERNAL;
 
                 // TODO(multi-display): Configure on per-display basis.
-                if (mService.mIsTouchDevice) {
+                if (mWmService.mIsTouchDevice) {
                     if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
                             InputDevice.SOURCE_TOUCHSCREEN) {
                         config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
@@ -1620,7 +1633,7 @@
             }
         }
 
-        if (config.navigation == Configuration.NAVIGATION_NONAV && mService.mHasPermanentDpad) {
+        if (config.navigation == Configuration.NAVIGATION_NONAV && mWmService.mHasPermanentDpad) {
             config.navigation = Configuration.NAVIGATION_DPAD;
             navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
         }
@@ -1628,10 +1641,10 @@
         // Determine whether a hard keyboard is available and enabled.
         // TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
         boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
-        if (hardKeyboardAvailable != mService.mHardKeyboardAvailable) {
-            mService.mHardKeyboardAvailable = hardKeyboardAvailable;
-            mService.mH.removeMessages(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
-            mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+        if (hardKeyboardAvailable != mWmService.mHardKeyboardAvailable) {
+            mWmService.mHardKeyboardAvailable = hardKeyboardAvailable;
+            mWmService.mH.removeMessages(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+            mWmService.mH.sendEmptyMessage(REPORT_HARD_KEYBOARD_STATUS_CHANGE);
         }
 
         mDisplayPolicy.updateConfigurationDependentBehaviors();
@@ -1640,7 +1653,7 @@
         config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
         config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
         config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
-        mService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
+        mWmService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
     }
 
     private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
@@ -1937,7 +1950,7 @@
     @Override
     void onAppTransitionDone() {
         super.onAppTransitionDone();
-        mService.mWindowsChanged = true;
+        mWmService.mWindowsChanged = true;
     }
 
     /**
@@ -1989,9 +2002,9 @@
 
     @Override
     int getOrientation() {
-        final WindowManagerPolicy policy = mService.mPolicy;
+        final WindowManagerPolicy policy = mWmService.mPolicy;
 
-        if (mService.mDisplayFrozen) {
+        if (mWmService.mDisplayFrozen) {
             if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
                         + " is frozen, return " + mLastWindowForcedOrientation);
@@ -2032,7 +2045,7 @@
     }
 
     void initializeDisplayBaseInfo() {
-        final DisplayManagerInternal displayManagerInternal = mService.mDisplayManagerInternal;
+        final DisplayManagerInternal displayManagerInternal = mWmService.mDisplayManagerInternal;
         if (displayManagerInternal != null) {
             // Bootstrap the default logical display from the display manager.
             final DisplayInfo newDisplayInfo = displayManagerInternal.getDisplayInfo(mDisplayId);
@@ -2055,7 +2068,7 @@
      */
     private void updateBaseDisplayMetricsIfNeeded() {
         // Get real display metrics without overrides from WM.
-        mService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
+        mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo);
         final int orientation = mDisplayInfo.rotation;
         final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270);
         final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
@@ -2084,7 +2097,7 @@
             mInitialDisplayHeight = newHeight;
             mInitialDisplayDensity = newDensity;
             mInitialDisplayCutout = newCutout;
-            mService.reconfigureDisplayLocked(this);
+            mWmService.reconfigureDisplayLocked(this);
         }
     }
 
@@ -2135,9 +2148,9 @@
     void setForcedDensity(int density, int userId) {
         final boolean clear = density == mInitialDisplayDensity;
         final boolean updateCurrent = userId == UserHandle.USER_CURRENT;
-        if (mService.mCurrentUserId == userId || updateCurrent) {
+        if (mWmService.mCurrentUserId == userId || updateCurrent) {
             mBaseDisplayDensity = density;
-            mService.reconfigureDisplayLocked(this);
+            mWmService.reconfigureDisplayLocked(this);
         }
         if (updateCurrent) {
             // We are applying existing settings so no need to save it again.
@@ -2147,7 +2160,7 @@
         if (density == mInitialDisplayDensity) {
             density = 0;
         }
-        mService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
+        mWmService.mDisplayWindowSettings.setForcedDensity(this, density, userId);
     }
 
     /** @param mode {@link #FORCE_SCALING_MODE_AUTO} or {@link #FORCE_SCALING_MODE_DISABLED}. */
@@ -2158,9 +2171,9 @@
 
         mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO);
         Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto"));
-        mService.reconfigureDisplayLocked(this);
+        mWmService.reconfigureDisplayLocked(this);
 
-        mService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
+        mWmService.mDisplayWindowSettings.setForcedScalingMode(this, mode);
     }
 
     /** If the given width and height equal to initial size, the setting will be cleared. */
@@ -2177,12 +2190,12 @@
 
         Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
         updateBaseDisplayMetrics(width, height, mBaseDisplayDensity);
-        mService.reconfigureDisplayLocked(this);
+        mWmService.reconfigureDisplayLocked(this);
 
         if (clear) {
             width = height = 0;
         }
-        mService.mDisplayWindowSettings.setForcedSize(this, width, height);
+        mWmService.mDisplayWindowSettings.setForcedSize(this, width, height);
     }
 
     void getStableRect(Rect out) {
@@ -2193,7 +2206,7 @@
         if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
                 + mDisplayId);
 
-        final TaskStack stack = new TaskStack(mService, stackId, controller);
+        final TaskStack stack = new TaskStack(mWmService, stackId, controller);
         mTaskStackContainers.addStackToDisplay(stack, onTop);
         return stack;
     }
@@ -2349,7 +2362,7 @@
     @Override
     void switchUser() {
         super.switchUser();
-        mService.mWindowsChanged = true;
+        mWmService.mWindowsChanged = true;
     }
 
     private void resetAnimationBackgroundAnimator() {
@@ -2377,24 +2390,24 @@
             mUnknownAppVisibilityController.clear();
             mAppTransition.removeAppTransitionTimeoutCallbacks();
             handleAnimatingStoppedAndTransition();
-            mService.stopFreezingDisplayLocked();
+            mWmService.stopFreezingDisplayLocked();
             super.removeImmediately();
             if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
             if (mPointerEventDispatcher != null && mTapDetector != null) {
                 unregisterPointerEventListener(mTapDetector);
-                unregisterPointerEventListener(mService.mMousePositionTracker);
+                unregisterPointerEventListener(mWmService.mMousePositionTracker);
                 mTapDetector = null;
             }
-            mService.mAnimator.removeDisplayLocked(mDisplayId);
+            mWmService.mAnimator.removeDisplayLocked(mDisplayId);
             mWindowingLayer.release();
             mOverlayLayer.release();
         } finally {
             mDisplayReady = false;
             mRemovingDisplay = false;
         }
+
         mDisplayPolicy.onDisplayRemoved();
-        mInputMonitor.onRemoved();
-        mService.mWindowPlacerLocked.requestTraversal();
+        mWmService.mWindowPlacerLocked.requestTraversal();
     }
 
     /** Returns true if a removal action is still being deferred. */
@@ -2471,7 +2484,7 @@
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
                 && !mDividerControllerLocked.isImeHideRequested();
         final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
+        final TaskStack imeTargetStack = mWmService.getImeFocusStackLocked();
         final int imeDockSide = (dockVisible && imeTargetStack != null) ?
                 imeTargetStack.getDockSide() : DOCKED_INVALID;
         final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
@@ -2598,13 +2611,12 @@
         mDisplayInfo.writeToProto(proto, DISPLAY_INFO);
         proto.write(ROTATION, mRotation);
         final ScreenRotationAnimation screenRotationAnimation =
-                mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+                mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
         if (screenRotationAnimation != null) {
             screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
         }
         mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
         mAppTransition.writeToProto(proto, APP_TRANSITION);
-        proto.write(SURFACE_SIZE, mSurfaceSize);
         if (mFocusedApp != null) {
             mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
         }
@@ -2725,6 +2737,8 @@
         mDisplayRotation.dump(prefix, pw);
         pw.println();
         mInputMonitor.dump(pw, "  ");
+        pw.println();
+        mInsetsStateController.dump(prefix, pw);
     }
 
     @Override
@@ -2842,11 +2856,11 @@
         }
 
         if (imWindowChanged) {
-            mService.mWindowsChanged = true;
+            mWmService.mWindowsChanged = true;
             setLayoutNeeded();
         }
 
-        if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+        if (DEBUG_FOCUS_LIGHT || mWmService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
                 + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
                 + " Callers=" + Debug.getCallers(4));
         final WindowState oldFocus = mCurrentFocus;
@@ -2885,7 +2899,7 @@
             if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                 performLayout(true /*initial*/, updateInputWindows);
             } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
-                mService.mRoot.performSurfacePlacement(false);
+                mWmService.mRoot.performSurfacePlacement(false);
             }
         }
 
@@ -2950,16 +2964,16 @@
     // moving containers or resizing them. Need to investigate the best way to have it automatically
     // happen so we don't run into issues with programmers forgetting to do it.
     void layoutAndAssignWindowLayersIfNeeded() {
-        mService.mWindowsChanged = true;
+        mWmService.mWindowsChanged = true;
         setLayoutNeeded();
 
-        if (!mService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
+        if (!mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                 false /*updateInputWindows*/)) {
             assignWindowLayers(false /* setLayoutNeeded */);
         }
 
         mInputMonitor.setUpdateInputWindowsNeededLw();
-        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
         mInputMonitor.updateInputWindowsLw(false /*force*/);
     }
 
@@ -2972,14 +2986,14 @@
             if (wsa.mSurfaceController == null) {
                 return;
             }
-            if (!mService.mSessions.contains(wsa.mSession)) {
+            if (!mWmService.mSessions.contains(wsa.mSession)) {
                 Slog.w(TAG_WM, "LEAKED SURFACE (session doesn't exist): "
                         + w + " surface=" + wsa.mSurfaceController
                         + " token=" + w.mToken
                         + " pid=" + w.mSession.mPid
                         + " uid=" + w.mSession.mUid);
                 wsa.destroySurface();
-                mService.mForceRemoves.add(w);
+                mWmService.mForceRemoves.add(w);
                 mTmpWindow = w;
             } else if (w.mAppToken != null && w.mAppToken.isClientHidden()) {
                 Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
@@ -3003,10 +3017,12 @@
         // Update display configuration for IME process.
         if (mInputMethodWindow != null) {
             final int imePid = mInputMethodWindow.mSession.mPid;
-            mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+            mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
                     mInputMethodWindow.getDisplayId());
         }
         computeImeTarget(true /* updateImeTarget */);
+        mInsetsStateController.getSourceProvider(TYPE_IME).setWindow(win,
+                null /* frameProvider */);
     }
 
     /**
@@ -3222,7 +3238,7 @@
      * Starts the Keyguard exit animation on all windows that don't belong to an app token.
      */
     void startKeyguardExitOnNonAppWindows(boolean onWallpaper, boolean goingToShade) {
-        final WindowManagerPolicy policy = mService.mPolicy;
+        final WindowManagerPolicy policy = mWmService.mPolicy;
         forAllWindows(w -> {
             if (w.mAppToken == null && policy.canBeHiddenByKeyguardLw(w)
                     && w.wouldBeVisibleIfPolicyIgnored() && !w.isVisible()) {
@@ -3251,7 +3267,7 @@
                 } else if (w.mAttrs.type == TYPE_WALLPAPER) {
                     mHaveWallpaper = true;
                 } else if (w.mAttrs.type == TYPE_STATUS_BAR) {
-                    mHaveKeyguard = mService.mPolicy.isKeyguardDrawnLw();
+                    mHaveKeyguard = mWmService.mPolicy.isKeyguardDrawnLw();
                 }
             }
             return false;
@@ -3264,28 +3280,28 @@
 
         // if the wallpaper service is disabled on the device, we're never going to have
         // wallpaper, don't bother waiting for it
-        boolean wallpaperEnabled = mService.mContext.getResources().getBoolean(
+        boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableWallpaperService)
-                && mService.mContext.getResources().getBoolean(
+                && mWmService.mContext.getResources().getBoolean(
                         com.android.internal.R.bool.config_checkWallpaperAtBoot)
-                && !mService.mOnlyCore;
+                && !mWmService.mOnlyCore;
 
         if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG_WM,
-                "******** booted=" + mService.mSystemBooted
-                + " msg=" + mService.mShowingBootMessages
+                "******** booted=" + mWmService.mSystemBooted
+                + " msg=" + mWmService.mShowingBootMessages
                 + " haveBoot=" + mHaveBootMsg + " haveApp=" + mHaveApp
                 + " haveWall=" + mHaveWallpaper + " wallEnabled=" + wallpaperEnabled
                 + " haveKeyguard=" + mHaveKeyguard);
 
         // If we are turning on the screen to show the boot message, don't do it until the boot
         // message is actually displayed.
-        if (!mService.mSystemBooted && !mHaveBootMsg) {
+        if (!mWmService.mSystemBooted && !mHaveBootMsg) {
             return true;
         }
 
         // If we are turning on the screen after the boot is completed normally, don't do so until
         // we have the application and wallpaper.
-        if (mService.mSystemBooted
+        if (mWmService.mSystemBooted
                 && ((!mHaveApp && !mHaveKeyguard) || (wallpaperEnabled && !mHaveWallpaper))) {
             return true;
         }
@@ -3351,7 +3367,7 @@
 
         mLastDispatchedSystemUiVisibility = visibility;
         if (isDefaultDisplay) {
-            mService.mInputManager.setSystemUiVisibility(visibility);
+            mWmService.mInputManager.setSystemUiVisibility(visibility);
         }
         updateSystemUiVisibility(visibility, globalDiff);
         return true;
@@ -3380,13 +3396,13 @@
     void reevaluateStatusBarVisibility() {
         int visibility = getDisplayPolicy().adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
         if (updateStatusBarVisibilityLocked(visibility)) {
-            mService.mWindowPlacerLocked.requestTraversal();
+            mWmService.mWindowPlacerLocked.requestTraversal();
         }
     }
 
     void onWindowFreezeTimeout() {
         Slog.w(TAG_WM, "Window freeze timeout expired.");
-        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
+        mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
 
         forAllWindows(w -> {
             if (!w.getOrientationChanging()) {
@@ -3394,28 +3410,28 @@
             }
             w.orientationChangeTimedOut();
             w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
-                    - mService.mDisplayFreezeTime);
+                    - mWmService.mDisplayFreezeTime);
             Slog.w(TAG_WM, "Force clearing orientation change: " + w);
         }, true /* traverseTopToBottom */);
-        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
     void waitForAllWindowsDrawn() {
-        final WindowManagerPolicy policy = mService.mPolicy;
+        final WindowManagerPolicy policy = mWmService.mPolicy;
         forAllWindows(w -> {
             final boolean keyguard = policy.isKeyguardHostWindow(w.mAttrs);
             if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
                 w.mWinAnimator.mDrawState = DRAW_PENDING;
                 // Force add to mResizingWindows.
                 w.resetLastContentInsets();
-                mService.mWaitingForDrawn.add(w);
+                mWmService.mWaitingForDrawn.add(w);
             }
         }, true /* traverseTopToBottom */);
     }
 
     // TODO: Super crazy long method that should be broken down...
     void applySurfaceChangesTransaction(boolean recoveringMemory) {
-        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
 
         mTmpUpdateAllDrawn.clear();
 
@@ -3462,6 +3478,7 @@
             pendingLayoutChanges |= mDisplayPolicy.finishPostLayoutPolicyLw();
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
+                mInsetsStateController.onPostLayout();
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
@@ -3471,7 +3488,7 @@
         prepareSurfaces();
 
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
-        mService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
+        mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                 mLastHasContent,
                 mTmpApplySurfaceChangesTransactionState.preferredRefreshRate,
                 mTmpApplySurfaceChangesTransactionState.preferredModeId,
@@ -3480,7 +3497,7 @@
         final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
         if (wallpaperVisible != mLastWallpaperVisible) {
             mLastWallpaperVisible = wallpaperVisible;
-            mService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
+            mWmService.mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(this);
         }
 
         while (!mTmpUpdateAllDrawn.isEmpty()) {
@@ -3529,10 +3546,6 @@
         }
     }
 
-    int getSurfaceSize() {
-        return mSurfaceSize;
-    }
-
     void performLayout(boolean initial, boolean updateInputWindows) {
         if (!isLayoutNeeded()) {
             return;
@@ -3582,7 +3595,7 @@
             mInputMonitor.updateInputWindowsLw(false /*force*/);
         }
 
-        mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
+        mWmService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
     }
 
     /**
@@ -3592,7 +3605,7 @@
      * @param config of the output bitmap
      */
     Bitmap screenshotDisplayLocked(Bitmap.Config config) {
-        if (!mService.mPolicy.isScreenOn()) {
+        if (!mWmService.mPolicy.isScreenOn()) {
             if (DEBUG_SCREENSHOT) {
                 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
             }
@@ -3620,7 +3633,7 @@
         convertCropForSurfaceFlinger(frame, rot, dw, dh);
 
         final ScreenRotationAnimation screenRotationAnimation =
-                mService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
+                mWmService.mAnimator.getScreenRotationAnimationLocked(DEFAULT_DISPLAY);
         final boolean inRotation = screenRotationAnimation != null &&
                 screenRotationAnimation.isAnimating();
         if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM, "Taking screenshot while rotating");
@@ -3675,11 +3688,12 @@
             }
             mTmpWindow = w;
             w.setDisplayLayoutNeeded();
-            mService.markForSeamlessRotation(w, false);
+            w.finishSeamlessRotation(true /* timeout */);
+            mWmService.markForSeamlessRotation(w, false);
         }, true /* traverseTopToBottom */);
 
         if (mTmpWindow != null) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
         }
     }
 
@@ -3707,20 +3721,20 @@
     @Override
     void onDescendantOverrideConfigurationChanged() {
         setLayoutNeeded();
-        mService.requestTraversal();
+        mWmService.requestTraversal();
     }
 
     boolean okToDisplay() {
         if (mDisplayId == DEFAULT_DISPLAY) {
-            return !mService.mDisplayFrozen
-                    && mService.mDisplayEnabled && mService.mPolicy.isScreenOn();
+            return !mWmService.mDisplayFrozen
+                    && mWmService.mDisplayEnabled && mWmService.mPolicy.isScreenOn();
         }
         return mDisplayInfo.state == Display.STATE_ON;
     }
 
     boolean okToAnimate() {
         return okToDisplay() &&
-                (mDisplayId != DEFAULT_DISPLAY || mService.mPolicy.okToAnimate());
+                (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate());
     }
 
     static final class TaskForResizePointSearchResult {
@@ -3938,7 +3952,7 @@
             } else if (stack == mSplitScreenPrimaryStack) {
                 mSplitScreenPrimaryStack = null;
                 // Re-set the split-screen create mode whenever the split-screen stack is removed.
-                mService.setDockedStackCreateStateLocked(
+                mWmService.setDockedStackCreateStateLocked(
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
                 mDividerControllerLocked.notifyDockedStackExistsChanged(false);
             }
@@ -4156,7 +4170,7 @@
             }
 
             final int orientation = super.getOrientation();
-            boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
+            boolean isCar = mWmService.mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_AUTOMOTIVE);
             if (isCar) {
                 // In a car, you cannot physically rotate the screen, so it doesn't make sense to
@@ -4337,10 +4351,10 @@
                 wt.assignLayer(t, j);
                 wt.assignChildLayers(t);
 
-                int layer = mService.mPolicy.getWindowLayerFromTypeLw(
+                int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
                         wt.windowType, wt.mOwnerCanManageAppTokens);
 
-                if (needAssignIme && layer >= mService.mPolicy.getWindowLayerFromTypeLw(
+                if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
                                 TYPE_INPUT_METHOD_DIALOG, true)) {
                     imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
                     needAssignIme = false;
@@ -4363,9 +4377,9 @@
          */
         private final Comparator<WindowToken> mWindowComparator = (token1, token2) ->
                 // Tokens with higher base layer are z-ordered on-top.
-                mService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
+                mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
                         token1.mOwnerCanManageAppTokens)
-                < mService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
+                < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
@@ -4395,7 +4409,7 @@
 
         @Override
         int getOrientation() {
-            final WindowManagerPolicy policy = mService.mPolicy;
+            final WindowManagerPolicy policy = mWmService.mPolicy;
             // Find a window requesting orientation.
             final WindowState win = getWindow(mGetOrientingWindow);
 
@@ -4403,7 +4417,7 @@
                 final int req = win.mAttrs.screenOrientation;
                 if (policy.isKeyguardHostWindow(win.mAttrs)) {
                     mLastKeyguardForcedOrientation = req;
-                    if (mService.mKeyguardGoingAway) {
+                    if (mWmService.mKeyguardGoingAway) {
                         // Keyguard can't affect the orientation if it is going away...
                         mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
                         return SCREEN_ORIENTATION_UNSET;
@@ -4461,7 +4475,7 @@
     };
 
     SurfaceControl.Builder makeSurface(SurfaceSession s) {
-        return mService.makeSurfaceBuilder(s)
+        return mWmService.makeSurfaceBuilder(s)
                 .setParent(mWindowingLayer);
     }
 
@@ -4473,9 +4487,7 @@
     @Override
     SurfaceControl.Builder makeChildSurface(WindowContainer child) {
         SurfaceSession s = child != null ? child.getSession() : getSession();
-        final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
-        b.setSize(mSurfaceSize, mSurfaceSize);
-
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s);
         if (child == null) {
             return b;
         }
@@ -4491,7 +4503,7 @@
      * and other potpourii.
      */
     SurfaceControl.Builder makeOverlay() {
-        return mService.makeSurfaceBuilder(mSession)
+        return mWmService.makeSurfaceBuilder(mSession)
             .setParent(mOverlayLayer);
     }
 
@@ -4590,7 +4602,7 @@
     @Override
     void prepareSurfaces() {
         final ScreenRotationAnimation screenRotationAnimation =
-                mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+                mWmService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
         if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
             screenRotationAnimation.getEnterTransformation().getMatrix().getValues(mTmpFloats);
             mPendingTransaction.setMatrix(mWindowingLayer,
@@ -4678,7 +4690,7 @@
                         + mDisplayId + " Callers=" + Debug.getCallers(5));
             }
             mAppTransition.setReady();
-            mService.mWindowPlacerLocked.requestTraversal();
+            mWmService.mWindowPlacerLocked.requestTraversal();
         }
     }
 
@@ -4708,7 +4720,7 @@
         mWallpaperMayChange = true;
         // Since the window list has been rebuilt, focus might have to be recomputed since the
         // actual order of windows might have changed again.
-        mService.mFocusMayChange = true;
+        mWmService.mFocusMayChange = true;
 
         pendingLayoutChanges |= changes;
     }
@@ -4721,7 +4733,19 @@
         return mDisplay.supportsSystemDecorations()
                 // TODO (b/111363427): Remove this and set the new FLAG_SHOULD_SHOW_LAUNCHER flag
                 // (b/114338689) whenever vr 2d display id is set.
-                || mDisplayId == mService.mVr2dDisplayId
-                || mService.mForceDesktopModeOnExternalDisplays;
+                || mDisplayId == mWmService.mVr2dDisplayId
+                || mWmService.mForceDesktopModeOnExternalDisplays;
+    }
+
+     /**
+     * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
+     * {@link #mOverlayLayer} to the specified surfaceControl.
+     *
+     * @param surfaceControlHandle The handle for the new SurfaceControl, where the DisplayContent's
+     *                             surfaces will be re-parented to.
+     */
+    void reparentDisplayContent(IBinder surfaceControlHandle) {
+        mPendingTransaction.reparent(mWindowingLayer, surfaceControlHandle)
+                .reparent(mOverlayLayer, surfaceControlHandle);
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c16f95e..581cec9 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
+import static android.view.InsetsState.TYPE_TOP_BAR;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -44,7 +45,6 @@
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
@@ -128,6 +128,7 @@
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.Surface;
@@ -804,6 +805,11 @@
                 if (mDisplayContent.isDefaultDisplay) {
                     mService.mPolicy.setKeyguardCandidateLw(win);
                 }
+                mDisplayContent.setInsetProvider(TYPE_TOP_BAR, win,
+                        (displayFrames, windowState, rect) -> {
+                            rect.top = 0;
+                            rect.bottom = getStatusBarHeight(displayFrames);
+                        });
                 break;
             case TYPE_NAVIGATION_BAR:
                 mContext.enforceCallingOrSelfPermission(
@@ -818,6 +824,8 @@
                 mNavigationBarController.setWindow(win);
                 mNavigationBarController.setOnBarVisibilityChangedListener(
                         mNavBarVisibilityListener, true);
+                mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR,
+                        win, null /* frameProvider */);
                 if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar);
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
@@ -845,9 +853,11 @@
             if (mDisplayContent.isDefaultDisplay) {
                 mService.mPolicy.setKeyguardCandidateLw(null);
             }
+            mDisplayContent.setInsetProvider(TYPE_TOP_BAR, null, null);
         } else if (mNavigationBar == win) {
             mNavigationBar = null;
             mNavigationBarController.setWindow(null);
+            mDisplayContent.setInsetProvider(InsetsState.TYPE_NAVIGATION_BAR, null, null);
         }
         if (mLastFocusedWindow == win) {
             mLastFocusedWindow = null;
@@ -855,6 +865,11 @@
         mScreenDecorWindows.remove(win);
     }
 
+    private int getStatusBarHeight(DisplayFrames displayFrames) {
+        return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation],
+                displayFrames.mDisplayCutoutSafe.top);
+    }
+
     /**
      * Control the animation to run when a window's state changes.  Return a
      * non-0 number to force the animation to a specific resource ID, or 0
@@ -2306,12 +2321,6 @@
                 && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             mTopDockedOpaqueOrDimmingWindowState = win;
         }
-
-        // Take note if a window wants to acquire a sleep token.
-        if ((attrs.privateFlags & PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN) != 0
-                && win.canAcquireSleepToken()) {
-            mWindowSleepTokenNeeded = true;
-        }
     }
 
     /**
@@ -2825,6 +2834,9 @@
             return 0;
         }
 
+        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(
+                mTopFullscreenOpaqueWindowState);
+
         int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
                 & ~mResettingSystemUiFlags
                 & ~mForceClearedSystemUiFlags;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index f1d1e49..7aabc15 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -30,6 +31,7 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -57,6 +59,7 @@
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private final DisplayPolicy mDisplayPolicy;
+    private final DisplayWindowSettings mDisplayWindowSettings;
     private final Context mContext;
     private final Object mLock;
 
@@ -71,10 +74,6 @@
     private StatusBarManagerInternal mStatusBarManagerInternal;
     private SettingsObserver mSettingsObserver;
 
-    // Default display does not rotate, apps that require non-default orientation will have to
-    // have the orientation emulated.
-    private boolean mForceDefaultOrientation;
-
     private int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
     @VisibleForTesting
@@ -93,6 +92,13 @@
     private int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
     private int mUserRotation = Surface.ROTATION_0;
 
+    /**
+     * A flag to indicate if the display rotation should be fixed to user specified rotation
+     * regardless of all other states (including app requrested orientation). {@code true} the
+     * display rotation should be fixed to user specified rotation, {@code false} otherwise.
+     */
+    private boolean mFixedToUserRotation;
+
     private int mDemoHdmiRotation;
     private int mDemoRotation;
     private boolean mDemoHdmiRotationLock;
@@ -100,15 +106,17 @@
 
     DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
         this(service, displayContent, displayContent.getDisplayPolicy(),
-                service.mContext, service.getWindowManagerLock());
+                service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
     }
 
     @VisibleForTesting
     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
-            DisplayPolicy displayPolicy, Context context, Object lock) {
+            DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
+            Context context, Object lock) {
         mService = service;
         mDisplayContent = displayContent;
         mDisplayPolicy = displayPolicy;
+        mDisplayWindowSettings = displayWindowSettings;
         mContext = context;
         mLock = lock;
         isDefaultDisplay = displayContent.isDefaultDisplay;
@@ -204,12 +212,19 @@
         // so if the orientation is forced, we need to respect that no matter what.
         final boolean isTv = mContext.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK);
-        mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) &&
-                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
-                // For debug purposes the next line turns this feature off with:
-                // $ adb shell setprop config.override_forced_orient true
-                // $ adb shell wm size reset
-                !"true".equals(SystemProperties.get("config.override_forced_orient"));
+        final boolean forceDefaultOrientationInRes =
+                res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation);
+        final boolean forceDefaultOrienation =
+                ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv)
+                        && forceDefaultOrientationInRes
+                        // For debug purposes the next line turns this feature off with:
+                        // $ adb shell setprop config.override_forced_orient true
+                        // $ adb shell wm size reset
+                        && !"true".equals(SystemProperties.get("config.override_forced_orient"));
+        // Configuration says we force to use the default orientation. We can fall back to fix
+        // rotation to only user rotation. As long as OEM doesn't change user rotation then the
+        // rotation of this display is effectively stuck at 0 deg.
+        setFixedToUserRotation(forceDefaultOrienation);
     }
 
     void setRotation(int rotation) {
@@ -227,7 +242,14 @@
         }
     }
 
-    void restoreUserRotation(int userRotationMode, int userRotation) {
+    void restoreSettings(int userRotationMode, int userRotation,
+            boolean fixedToUserRotation) {
+        mFixedToUserRotation = fixedToUserRotation;
+
+        // We will retrieve user rotation and user rotation mode from settings for default display.
+        if (isDefaultDisplay) {
+            return;
+        }
         if (userRotationMode != WindowManagerPolicy.USER_ROTATION_FREE
                 && userRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) {
             Slog.w(TAG, "Trying to restore an invalid user rotation mode " + userRotationMode
@@ -243,6 +265,18 @@
         mUserRotation = userRotation;
     }
 
+    void setFixedToUserRotation(boolean fixedToUserRotation) {
+        if (mFixedToUserRotation == fixedToUserRotation) {
+            return;
+        }
+
+        mFixedToUserRotation = fixedToUserRotation;
+        mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent,
+                fixedToUserRotation);
+        mService.updateRotation(true /* alwaysSendConfiguration */,
+                false /* forceRelayout */);
+    }
+
     private void setUserRotation(int userRotationMode, int userRotation) {
         if (isDefaultDisplay) {
             // We'll be notified via settings listener, so we don't need to update internal values.
@@ -265,7 +299,7 @@
             mUserRotation = userRotation;
             changed = true;
         }
-        mService.mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
+        mDisplayWindowSettings.setUserRotation(mDisplayContent, userRotationMode,
                 userRotation);
         if (changed) {
             mService.updateRotation(true /* alwaysSendConfiguration */,
@@ -291,9 +325,8 @@
                 Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) == 0;
     }
 
-    /** @return true if com.android.internal.R.bool#config_forceDefaultOrientation is true. */
-    boolean isDefaultOrientationForced() {
-        return mForceDefaultOrientation;
+    boolean isFixedToUserRotation() {
+        return mFixedToUserRotation;
     }
 
     public int getLandscapeRotation() {
@@ -399,6 +432,12 @@
      * screen is switched off.
      */
     private boolean needSensorRunning() {
+        if (mFixedToUserRotation) {
+            // We are sure we only respect user rotation settings, so we are sure we will not
+            // support sensor rotation.
+            return false;
+        }
+
         if (mSupportAutoRotation) {
             if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
                     || mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
@@ -459,8 +498,8 @@
                         );
         }
 
-        if (mForceDefaultOrientation) {
-            return Surface.ROTATION_0;
+        if (mFixedToUserRotation) {
+            return mUserRotation;
         }
 
         int sensorRotation = mOrientationListener != null
@@ -701,8 +740,8 @@
         // demo, hdmi, vr, etc mode.
 
         // Determine if the rotation is currently forced.
-        if (mForceDefaultOrientation) {
-            return false; // Rotation is forced to default orientation.
+        if (mFixedToUserRotation) {
+            return false; // Rotation is forced to user settings.
         }
 
         final int lidState = mDisplayPolicy.getLidState();
@@ -861,6 +900,7 @@
         pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock);
         pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation));
         pw.println(prefix + "  mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
+        pw.println(prefix + "  mFixedToUserRotation=" + mFixedToUserRotation);
     }
 
     private class OrientationListener extends WindowOrientationListener {
@@ -945,4 +985,10 @@
             }
         }
     }
+
+    @VisibleForTesting
+    interface ContentObserverRegister {
+        void registerContentObserver(Uri uri, boolean notifyForDescendants,
+                ContentObserver observer, @UserIdInt int userHandle);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 864f7e1..55b3def 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -90,7 +90,7 @@
     public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
         synchronized (mGlobalLock) {
             if (mContainer != null) {
-                mContainer.mService.setNewDisplayOverrideConfiguration(overrideConfiguration,
+                mContainer.mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration,
                         mContainer);
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index f7dfd3f..45d77de 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -80,6 +80,7 @@
         private boolean mShouldShowWithInsecureKeyguard = false;
         private boolean mShouldShowSystemDecors = false;
         private boolean mShouldShowIme = false;
+        private boolean mFixedToUserRotation;
 
         private Entry(String name) {
             mName = name;
@@ -97,7 +98,8 @@
                     && mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED
                     && !mShouldShowWithInsecureKeyguard
                     && !mShouldShowSystemDecors
-                    && !mShouldShowIme;
+                    && !mShouldShowIme
+                    && !mFixedToUserRotation;
         }
     }
 
@@ -186,6 +188,13 @@
         writeSettingsIfNeeded(entry, displayInfo);
     }
 
+    void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) {
+        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+        final Entry entry = getOrCreateEntry(displayInfo);
+        entry.mFixedToUserRotation = fixedToUserRotation;
+        writeSettingsIfNeeded(entry, displayInfo);
+    }
+
     private int getWindowingModeLocked(Entry entry, int displayId) {
         int windowingMode = entry != null ? entry.mWindowingMode
                 : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -331,7 +340,8 @@
         displayInfo.overscanRight = entry.mOverscanRight;
         displayInfo.overscanBottom = entry.mOverscanBottom;
 
-        dc.getDisplayRotation().restoreUserRotation(entry.mUserRotationMode, entry.mUserRotation);
+        dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
+                entry.mUserRotation, entry.mFixedToUserRotation);
 
         if (entry.mForcedDensity != 0) {
             dc.mBaseDisplayDensity = entry.mForcedDensity;
@@ -458,6 +468,8 @@
                     "shouldShowWithInsecureKeyguard");
             entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors");
             entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme");
+            entry.mFixedToUserRotation = getBooleanAttribute(parser,
+                    "fixedToUserRotation");
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -541,6 +553,10 @@
                 if (entry.mShouldShowIme) {
                     out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme));
                 }
+                if (entry.mFixedToUserRotation) {
+                    out.attribute(null, "fixedToUserRotation",
+                            Boolean.toString(entry.mFixedToUserRotation));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 7ed078a..a667d67 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -31,13 +31,12 @@
 import android.view.Display;
 import android.view.IWindow;
 import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
 import android.view.View;
 
 import com.android.internal.util.Preconditions;
-import android.view.InputWindowHandle;
 import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
+
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -71,11 +70,18 @@
             new IDragDropCallback() {});
 
     boolean dragDropActiveLocked() {
-        return mDragState != null;
+        return mDragState != null && !mDragState.isClosing();
     }
 
-    InputWindowHandle getInputWindowHandleLocked() {
-        return mDragState.getInputWindowHandle();
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        mDragState.showInputSurface(t, displayId);
+    }
+
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mDragState != null) {
+            // TODO: Are we guaranteed to get here?
+            mDragState.hideInputSurface(t, displayId);
+        }
     }
 
     void registerCallback(IDragDropCallback callback) {
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index a379266..607ee76 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -31,23 +31,24 @@
 import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.ClipDescription;
-import android.content.Context;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.hardware.input.InputManager;
+import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.IUserManager;
 import android.os.UserManagerInternal;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DragEvent;
+import android.view.InputApplicationHandle;
 import android.view.InputChannel;
 import android.view.InputDevice;
+import android.view.InputWindowHandle;
 import android.view.PointerIcon;
 import android.view.SurfaceControl;
 import android.view.View;
@@ -57,8 +58,6 @@
 
 import com.android.internal.view.IDragAndDropPermissions;
 import com.android.server.LocalServices;
-import android.view.InputApplicationHandle;
-import android.view.InputWindowHandle;
 
 import java.util.ArrayList;
 
@@ -118,6 +117,17 @@
     private final Interpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
     private Point mDisplaySize = new Point();
 
+    // A surface used to catch input events for the drag-and-drop operation.
+    SurfaceControl mInputSurface;
+
+    private final Rect mTmpClipRect = new Rect();
+
+    /**
+     * Whether we are finishing this drag and drop. This starts with {@code false}, and is set to
+     * {@code true} when {@link #closeLocked()} is called.
+     */
+    private boolean mIsClosing;
+
     DragState(WindowManagerService service, DragDropController controller, IBinder token,
             SurfaceControl surface, int flags, IBinder localWin) {
         mService = service;
@@ -127,6 +137,46 @@
         mFlags = flags;
         mLocalWin = localWin;
         mNotifiedWindows = new ArrayList<WindowState>();
+
+    }
+
+    boolean isClosing() {
+        return mIsClosing;
+    }
+
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (displayId != mDisplayContent.getDisplayId()) {
+            return;
+        }
+
+        if (mInputSurface != null) {
+            t.hide(mInputSurface);
+        }
+    }
+
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (displayId != mDisplayContent.getDisplayId()) {
+            return;
+        }
+
+        if (mInputSurface == null) {
+            mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+                    .getSession()).setContainerLayer(true)
+                    .setName("Drag and Drop Input Consumer").build();
+        }
+        final InputWindowHandle h = getInputWindowHandle();
+        if (h == null) {
+            Slog.w(TAG_WM, "Drag is in progress but there is no "
+                    + "drag window handle.");
+            return;
+        }
+
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, h);
+        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+        mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
     }
 
     /**
@@ -134,6 +184,7 @@
      * DragDropController#mDragState becomes null.
      */
     void closeLocked() {
+        mIsClosing = true;
         // Unregister the input interceptor.
         if (mInputInterceptor != null) {
             if (DEBUG_DRAG)
@@ -218,7 +269,7 @@
             mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
                     mService.mH.getLooper(), mDragDropController);
 
-            mDragApplicationHandle = new InputApplicationHandle(null);
+            mDragApplicationHandle = new InputApplicationHandle(new Binder());
             mDragApplicationHandle.name = "drag";
             mDragApplicationHandle.dispatchingTimeoutNanos =
                     WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -226,7 +277,7 @@
             mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                     display.getDisplayId());
             mDragWindowHandle.name = "drag";
-            mDragWindowHandle.inputChannel = mServerChannel;
+            mDragWindowHandle.token = mServerChannel.getToken();
             mDragWindowHandle.layer = getDragLayerLocked();
             mDragWindowHandle.layoutParamsFlags = 0;
             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index fddf6ca..7cb4a43 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -32,7 +31,6 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class EmulatorDisplayOverlay {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM;
@@ -59,7 +57,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("EmulatorDisplayOverlay")
-                    .setSize(mScreenSize.x, mScreenSize.y)
+                    .setBufferSize(mScreenSize.x, mScreenSize.y)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayer(zOrder);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 49bedc9..4df5a0b 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import android.graphics.Rect;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -25,6 +27,8 @@
 
 import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.util.Slog;
 
 import java.io.PrintWriter;
 
@@ -39,6 +43,9 @@
     final int mClientPid;
     final UserHandle mClientUser;
 
+    final SurfaceControl mInputSurface;
+    Rect mTmpClipRect = new Rect();
+
     InputConsumerImpl(WindowManagerService service, IBinder token, String name,
             InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
         mService = service;
@@ -58,14 +65,14 @@
         }
         mService.mInputManager.registerInputChannel(mServerChannel, null);
 
-        mApplicationHandle = new InputApplicationHandle(null);
+        mApplicationHandle = new InputApplicationHandle(new Binder());
         mApplicationHandle.name = name;
         mApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
 
         mWindowHandle = new InputWindowHandle(mApplicationHandle, null, displayId);
         mWindowHandle.name = name;
-        mWindowHandle.inputChannel = mServerChannel;
+        mWindowHandle.token = mServerChannel.getToken();
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
         mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
         mWindowHandle.layoutParamsFlags = 0;
@@ -80,6 +87,10 @@
         mWindowHandle.ownerUid = Process.myUid();
         mWindowHandle.inputFeatures = 0;
         mWindowHandle.scaleFactor = 1.0f;
+
+        mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId)
+                .getSession()).setContainerLayer(true).setName("Input Consumer " + name)
+                .build();
     }
 
     void linkToDeathRecipient() {
@@ -102,12 +113,33 @@
         mToken.unlinkToDeath(this, 0);
     }
 
-    void layout(int dw, int dh) {
-        mWindowHandle.touchableRegion.set(0, 0, dw, dh);
-        mWindowHandle.frameLeft = 0;
-        mWindowHandle.frameTop = 0;
-        mWindowHandle.frameRight = dw;
-        mWindowHandle.frameBottom = dh;
+    void layout(SurfaceControl.Transaction t, int dw, int dh) {
+        t.setPosition(mInputSurface, 0, 0);
+
+        mTmpClipRect.set(0, 0, dw, dh);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
+    void layout(SurfaceControl.Transaction t, Rect r) {
+        t.setPosition(mInputSurface, r.left, r.top);
+        mTmpClipRect.set(0, 0, r.width(), r.height());
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
+    void hide(SurfaceControl.Transaction t) {
+        t.hide(mInputSurface);
+    }
+
+    void show(SurfaceControl.Transaction t, WindowState w) {
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, mWindowHandle);
+        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+    }
+
+    void show(SurfaceControl.Transaction t, int layer) {
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, mWindowHandle);
+        t.setLayer(mInputSurface, layer);
     }
 
     private int getLayerLw(int windowType) {
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 92ea1a9..639ed02 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -6,21 +6,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
-import android.app.ActivityManager;
 import android.os.Debug;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Slog;
+import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
-import android.view.InputApplicationHandle;
 import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
-import android.view.InputChannel;
 
 import java.io.PrintWriter;
-import java.util.HashMap;
 
 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
     private final WindowManagerService mService;
@@ -72,8 +67,7 @@
      * Called by the InputManager.
      */
     @Override
-    public long notifyANR(InputApplicationHandle inputApplicationHandle,
-            IBinder token, String reason) {
+    public long notifyANR(IBinder token, String reason) {
         AppWindowToken appWindowToken = null;
         WindowState windowState = null;
         boolean aboveSystem = false;
@@ -84,9 +78,6 @@
                     appWindowToken = windowState.mAppToken;
                 }
             }
-            if (appWindowToken == null && inputApplicationHandle != null) {
-                appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
-            }
 
             if (windowState != null) {
                 Slog.i(TAG_WM, "Input event dispatching timed out "
@@ -116,9 +107,7 @@
         if (appWindowToken != null && appWindowToken.appToken != null) {
             // Notify the activity manager about the timeout and let it decide whether
             // to abort dispatching or keep waiting.
-            final AppWindowContainerController controller = appWindowToken.getController();
-            final boolean abort = controller != null
-                    && controller.keyDispatchingTimedOut(reason,
+            final boolean abort = appWindowToken.keyDispatchingTimedOut(reason,
                     (windowState != null) ? windowState.mSession.mPid : -1);
             if (!abort) {
                 // The activity manager declined to abort dispatching.
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 83d32c8ad..88b22cb 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -44,6 +44,7 @@
 import android.view.InputChannel;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
 
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -61,9 +62,8 @@
     // When true, need to call updateInputWindowsLw().
     private boolean mUpdateInputWindowsNeeded = true;
 
-    // Array of window handles to provide to the input dispatcher.
-    private InputWindowHandle[] mInputWindowHandles;
-    private int mInputWindowHandleCount;
+    // Currently focused input window handle.
+    private InputWindowHandle mFocusedInputWindowHandle;
 
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
@@ -72,6 +72,8 @@
 
     private int mDisplayId;
 
+    SurfaceControl.Transaction mInputTransaction = new SurfaceControl.Transaction();
+
     /**
      * The set of input consumer added to the window manager by name, which consumes input events
      * for the windows below it.
@@ -126,6 +128,7 @@
     private boolean disposeInputConsumer(InputConsumerImpl consumer) {
         if (consumer != null) {
             consumer.disposeChannelsLw();
+            consumer.hide(mInputTransaction);
             return true;
         }
         return false;
@@ -137,7 +140,16 @@
 
     void layoutInputConsumers(int dw, int dh) {
         for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
-            mInputConsumers.valueAt(i).layout(dw, dh);
+            mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
+        }
+    }
+
+    // The visibility of the input consumers is recomputed each time we
+    // update the input windows. We use a model where consumers begin invisible
+    // (set so by this function) and must meet some condition for visibility on each update.
+    void resetInputConsumers(SurfaceControl.Transaction t) {
+        for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
+            mInputConsumers.valueAt(i).hide(t);
         }
     }
 
@@ -177,18 +189,7 @@
     }
 
 
-    private void addInputWindowHandle(final InputWindowHandle windowHandle) {
-        if (mInputWindowHandles == null) {
-            mInputWindowHandles = new InputWindowHandle[16];
-        }
-        if (mInputWindowHandleCount >= mInputWindowHandles.length) {
-            mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,
-                    mInputWindowHandleCount * 2);
-        }
-        mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;
-    }
-
-    void addInputWindowHandle(final InputWindowHandle inputWindowHandle,
+    void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
             final WindowState child, int flags, final int type, final boolean isVisible,
             final boolean hasFocus, final boolean hasWallpaper) {
         // Add a window to our list of input windows.
@@ -214,6 +215,11 @@
         inputWindowHandle.frameRight = frame.right;
         inputWindowHandle.frameBottom = frame.bottom;
 
+        // Surface insets are hardcoded to be the same in all directions
+        // and we could probably deprecate the "left/right/top/bottom" concept.
+        // we avoid reintroducing this concept by just choosing one of them here.
+        inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;
+
         if (child.mGlobalScale != 1) {
             // If we are scaling the window, input coordinates need
             // to be inversely scaled to map from what is on screen
@@ -227,12 +233,9 @@
             Slog.d(TAG_WM, "addInputWindowHandle: "
                     + child + ", " + inputWindowHandle);
         }
-        addInputWindowHandle(inputWindowHandle);
-    }
 
-    private void clearInputWindowHandlesLw() {
-        while (mInputWindowHandleCount != 0) {
-            mInputWindowHandles[--mInputWindowHandleCount] = null;
+        if (hasFocus) {
+            mFocusedInputWindowHandle = inputWindowHandle;
         }
     }
 
@@ -261,14 +264,9 @@
             if (DEBUG_DRAG) {
                 Log.d(TAG_WM, "Inserting drag window");
             }
-            final InputWindowHandle dragWindowHandle =
-                    mService.mDragDropController.getInputWindowHandleLocked();
-            if (dragWindowHandle == null) {
-                Slog.w(TAG_WM, "Drag is in progress but there is no "
-                        + "drag window handle.");
-            } else if (dragWindowHandle.displayId == mDisplayId) {
-                addInputWindowHandle(dragWindowHandle);
-            }
+            mService.mDragDropController.showInputSurface(mInputTransaction, mDisplayId);
+        } else {
+            mService.mDragDropController.hideInputSurface(mInputTransaction, mDisplayId);
         }
 
         final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
@@ -276,14 +274,9 @@
             if (DEBUG_TASK_POSITIONING) {
                 Log.d(TAG_WM, "Inserting window handle for repositioning");
             }
-            final InputWindowHandle dragWindowHandle =
-                    mService.mTaskPositioningController.getDragWindowHandleLocked();
-            if (dragWindowHandle == null) {
-                Slog.e(TAG_WM,
-                        "Repositioning is in progress but there is no drag window handle.");
-            } else if (dragWindowHandle.displayId == mDisplayId) {
-                addInputWindowHandle(dragWindowHandle);
-            }
+            mService.mTaskPositioningController.showInputSurface(mInputTransaction, mDisplayId);
+        } else {
+            mService.mTaskPositioningController.hideInputSurface(mInputTransaction, mDisplayId);
         }
 
         // Add all windows on the default display.
@@ -362,12 +355,6 @@
         }
     }
 
-    void onRemoved() {
-        // If DisplayContent removed, we need find a way to remove window handles of this display
-        // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
-        mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-    }
-
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
         InputConsumerImpl navInputConsumer;
         InputConsumerImpl pipInputConsumer;
@@ -401,16 +388,16 @@
             final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
             wallpaperController = dc.mWallpaperController;
 
-            dc.forAllWindows(this, true /* traverseTopToBottom */);
+            resetInputConsumers(mInputTransaction);
+
+            dc.forAllWindows(this,
+                    true /* traverseTopToBottom */);
+
             if (mAddWallpaperInputConsumerHandle) {
-                // No visible wallpaper found, add the wallpaper input consumer at the end.
-                addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                wallpaperInputConsumer.show(mInputTransaction, 0);
             }
 
-            // Send windows to native code.
-            mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
-
-            clearInputWindowHandlesLw();
+            mInputTransaction.apply();
 
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
@@ -420,7 +407,7 @@
             final InputChannel inputChannel = w.mInputChannel;
             final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
             if (inputChannel == null || inputWindowHandle == null || w.mRemoved
-                    || w.canReceiveTouchInput()) {
+                    || w.cantReceiveTouchInput()) {
                 // Skip this window because it cannot possibly receive input.
                 return;
             }
@@ -438,41 +425,36 @@
                         && recentsAnimationController.shouldApplyInputConsumer(w.mAppToken)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
                             recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
-                        addInputWindowHandle(recentsAnimationInputConsumer.mWindowHandle);
+                        recentsAnimationInputConsumer.show(mInputTransaction, w);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
-                    // If the target app window does not yet exist, then we don't add the input
-                    // consumer window, but also don't add the app window below.
-                    return;
                 }
             }
 
             if (w.inPinnedWindowingMode()) {
-                if (mAddPipInputConsumerHandle
-                        && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
+                if (mAddPipInputConsumerHandle) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
                     w.getBounds(mTmpRect);
+                    pipInputConsumer.layout(mInputTransaction, mTmpRect);
+
+                    // The touchable region is relative to the surface top-left
+                    mTmpRect.offsetTo(0, 0);
                     pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
-                    addInputWindowHandle(pipInputConsumer.mWindowHandle);
+                    pipInputConsumer.show(mInputTransaction, w);
                     mAddPipInputConsumerHandle = false;
                 }
-                // TODO: Fix w.canReceiveTouchInput() to handle this case
-                if (!hasFocus) {
-                    // Skip this pinned stack window if it does not have focus
-                    return;
-                }
             }
 
             if (mAddInputConsumerHandle
                     && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
-                addInputWindowHandle(navInputConsumer.mWindowHandle);
+                navInputConsumer.show(mInputTransaction, w);
                 mAddInputConsumerHandle = false;
             }
 
             if (mAddWallpaperInputConsumerHandle) {
                 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
                     // Add the wallpaper input consumer above the first visible wallpaper.
-                    addInputWindowHandle(wallpaperInputConsumer.mWindowHandle);
+                    wallpaperInputConsumer.show(mInputTransaction, w);
                     mAddWallpaperInputConsumerHandle = false;
                 }
             }
@@ -490,8 +472,13 @@
                 mService.mDragDropController.sendDragStartedIfNeededLocked(w);
             }
 
-            addInputWindowHandle(
+            populateInputWindowHandle(
                     inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+
+            if (w.mWinAnimator.hasSurface()) {
+                mInputTransaction.setInputWindowInfo(
+                        w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
new file mode 100644
index 0000000..282838f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
+import android.view.InsetsSource;
+import android.view.InsetsSourceControl;
+
+import com.android.internal.util.function.TriConsumer;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+
+import java.io.PrintWriter;
+
+/**
+ * Controller for a specific inset source on the server. It's called provider as it provides the
+ * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ */
+class InsetsSourceProvider {
+
+    private final Rect mTmpRect = new Rect();
+    private final @NonNull InsetsSource mSource;
+    private final DisplayContent mDisplayContent;
+    private final InsetsStateController mStateController;
+    private @Nullable InsetsSourceControl mControl;
+    private @Nullable WindowState mControllingWin;
+    private @Nullable ControlAdapter mAdapter;
+    private WindowState mWin;
+    private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
+
+    InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
+            DisplayContent displayContent) {
+        mSource = source;
+        mDisplayContent = displayContent;
+        mStateController = stateController;
+    }
+
+    InsetsSource getSource() {
+        return mSource;
+    }
+
+    /**
+     * Updates the window that currently backs this source.
+     *
+     * @param win The window that links to this source.
+     * @param frameProvider Based on display frame state and the window, calculates the resulting
+     *                      frame that should be reported to clients.
+     */
+    void setWindow(@Nullable WindowState win,
+            @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider) {
+        if (mWin != null) {
+            mWin.setInsetProvider(null);
+        }
+        mWin = win;
+        mFrameProvider = frameProvider;
+        if (win == null) {
+            mSource.setVisible(false);
+            mSource.setFrame(new Rect());
+        } else {
+            mSource.setVisible(true);
+            mWin.setInsetProvider(this);
+        }
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        if (mWin == null) {
+            return;
+        }
+
+        mTmpRect.set(mWin.getFrameLw());
+        if (mFrameProvider != null) {
+            mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
+        } else {
+            mTmpRect.inset(mWin.mGivenContentInsets);
+        }
+        mSource.setFrame(mTmpRect);
+        mSource.setVisible(mWin.isVisible() && !mWin.mGivenInsetsPending);
+
+    }
+
+    void updateControlForTarget(@Nullable WindowState target) {
+        if (target == mControllingWin) {
+            return;
+        }
+        if (target == null) {
+            revokeControl();
+            return;
+        }
+        mAdapter = new ControlAdapter();
+        mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
+                false /* TODO hidden */);
+        mControllingWin = target;
+        mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash);
+    }
+
+    InsetsSourceControl getControl() {
+        return mControl;
+    }
+
+    void revokeControl() {
+        if (mControllingWin != null) {
+
+            // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
+            mWin.cancelAnimation();
+        }
+    }
+
+    private class ControlAdapter implements AnimationAdapter {
+
+        private SurfaceControl mCapturedLeash;
+
+        @Override
+        public boolean getShowWallpaper() {
+            return false;
+        }
+
+        @Override
+        public int getBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void startAnimation(SurfaceControl animationLeash, Transaction t,
+                OnAnimationFinishedCallback finishCallback) {
+            mCapturedLeash = animationLeash;
+            t.setPosition(mCapturedLeash, mSource.getFrame().left, mSource.getFrame().top);
+        }
+
+        @Override
+        public void onAnimationCancelled(SurfaceControl animationLeash) {
+            if (mAdapter == this) {
+                mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+                mControl = null;
+                mControllingWin = null;
+                mAdapter = null;
+            }
+        }
+
+        @Override
+        public long getDurationHint() {
+            return 0;
+        }
+
+        @Override
+        public long getStatusBarTransitionsStartTime() {
+            return 0;
+        }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+        }
+
+        @Override
+        public void writeToProto(ProtoOutputStream proto) {
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
new file mode 100644
index 0000000..592b7fb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.ViewRootImpl;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Manages global window inset state in the system represented by {@link InsetsState}.
+ */
+class InsetsStateController {
+
+    private final InsetsState mLastState = new InsetsState();
+    private final InsetsState mState = new InsetsState();
+    private final DisplayContent mDisplayContent;
+
+    private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
+    private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
+    private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
+    private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
+
+    private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
+        if (w.isVisible()) {
+            w.notifyInsetsChanged();
+        }
+    };
+
+    InsetsStateController(DisplayContent displayContent) {
+        mDisplayContent = displayContent;
+    }
+
+    /**
+     * When dispatching window state to the client, we'll need to exclude the source that represents
+     * the window that is being dispatched.
+     *
+     * @param target The client we dispatch the state to.
+     * @return The state stripped of the necessary information.
+     */
+    InsetsState getInsetsForDispatch(WindowState target) {
+        final InsetsSourceProvider provider = target.getInsetProvider();
+        if (provider == null) {
+            return mState;
+        }
+
+        final InsetsState state = new InsetsState();
+        state.set(mState);
+        final int type = provider.getSource().getType();
+        state.removeSource(type);
+
+        // Navigation bar doesn't get influenced by anything else
+        if (type == TYPE_NAVIGATION_BAR) {
+            state.removeSource(TYPE_IME);
+            state.removeSource(TYPE_TOP_BAR);
+        }
+        return state;
+    }
+
+    @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
+        ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
+        if (controlled == null) {
+            return null;
+        }
+        final int size = controlled.size();
+        final InsetsSourceControl[] result = new InsetsSourceControl[size];
+        for (int i = 0; i < size; i++) {
+            result[i] = mControllers.get(controlled.get(i)).getControl();
+        }
+        return result;
+    }
+
+    /**
+     * @return The provider of a specific type.
+     */
+    InsetsSourceProvider getSourceProvider(int type) {
+        return mControllers.computeIfAbsent(type,
+                key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
+    }
+
+    /**
+     * Called when a layout pass has occurred.
+     */
+    void onPostLayout() {
+        for (int i = mControllers.size() - 1; i>= 0; i--) {
+            mControllers.valueAt(i).onPostLayout();
+        }
+        if (!mLastState.equals(mState)) {
+            mLastState.set(mState, true /* copySources */);
+            notifyInsetsChanged();
+        }
+    }
+
+    void onImeTargetChanged(@Nullable WindowState imeTarget) {
+        onControlChanged(TYPE_IME, imeTarget);
+        notifyPendingInsetsControlChanged();
+    }
+
+    /**
+     * Called when the top opaque fullscreen window that is able to control the system bars changes.
+     *
+     * @param controllingWindow The window that is now able to control the system bars appearance
+     *                          and visibility.
+     */
+    void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
+        // TODO: Apply policy that determines whether controllingWindow is able to control system
+        // bars
+
+        // TODO: Depending on the form factor, mapping is different
+        onControlChanged(TYPE_TOP_BAR, controllingWindow);
+        onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
+        notifyPendingInsetsControlChanged();
+    }
+
+    void notifyControlRevoked(@NonNull WindowState previousControllingWin,
+            InsetsSourceProvider provider) {
+        removeFromControlMaps(previousControllingWin, provider.getSource().getType());
+    }
+
+    private void onControlChanged(int type, @Nullable WindowState win) {
+        if (!ViewRootImpl.USE_NEW_INSETS) {
+            return;
+        }
+        final WindowState previous = mTypeWinControlMap.get(type);
+        if (win == previous) {
+            return;
+        }
+        final InsetsSourceProvider controller = mControllers.get(type);
+        if (controller == null) {
+            return;
+        }
+        controller.updateControlForTarget(win);
+        if (previous != null) {
+            removeFromControlMaps(previous, type);
+            mPendingControlChanged.add(previous);
+        }
+        if (win != null) {
+            addToControlMaps(win, type);
+            mPendingControlChanged.add(win);
+        }
+    }
+
+    private void removeFromControlMaps(@NonNull WindowState win, int type) {
+        final ArrayList<Integer> array = mWinControlTypeMap.get(win);
+        if (array == null) {
+            return;
+        }
+        array.remove((Integer) type);
+        if (array.isEmpty()) {
+            mWinControlTypeMap.remove(win);
+        }
+        mTypeWinControlMap.remove(type);
+    }
+
+    private void addToControlMaps(@NonNull WindowState win, int type) {
+        final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
+                key -> new ArrayList<>());
+        array.add(type);
+        mTypeWinControlMap.put(type, win);
+    }
+
+    private void notifyPendingInsetsControlChanged() {
+        mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
+            for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
+                final WindowState controllingWin = mPendingControlChanged.valueAt(i);
+                controllingWin.notifyInsetsControlChanged();
+            }
+            mPendingControlChanged.clear();
+        });
+    }
+
+    private void notifyInsetsChanged() {
+        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
+    }
+
+    void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "WindowInsetsStateController");
+        mState.dump(prefix + "  ", pw);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c91af73..4ef3513 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -72,6 +72,7 @@
     private int mVisibilityTransactionDepth;
     private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
     private final ActivityTaskManagerService mService;
+    private RootActivityContainer mRootActivityContainer;
 
     KeyguardController(ActivityTaskManagerService service,
             ActivityStackSupervisor stackSupervisor) {
@@ -81,6 +82,7 @@
 
     void setWindowManager(WindowManagerService windowManager) {
         mWindowManager = windowManager;
+        mRootActivityContainer = mService.mRootActivityContainer;
     }
 
     /**
@@ -146,7 +148,7 @@
                 mDismissalRequested = false;
             }
         }
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+        mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         updateKeyguardSleepToken();
     }
 
@@ -172,16 +174,17 @@
         mWindowManager.deferSurfaceLayout();
         try {
             setKeyguardGoingAway(true);
-            mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+            mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
                     .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY,
                             false /* alwaysKeepCurrent */, convertTransitFlags(flags),
                             false /* forceOverride */);
             updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
-            mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-            mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
+            mRootActivityContainer.resumeFocusedStacksTopActivities();
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mRootActivityContainer.addStartingWindowsForVisibleActivities(
+                    true /* taskSwitch */);
             mWindowManager.executeAppTransition();
         } finally {
             Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
@@ -277,8 +280,9 @@
 
     private void visibilitiesUpdated() {
         boolean requestDismissKeyguard = false;
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+        for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
             final KeyguardDisplayState state = getDisplay(display.mDisplayId);
             state.visibilitiesUpdated(this, display);
             requestDismissKeyguard |= state.mRequestDismissKeyguard;
@@ -298,12 +302,12 @@
         if (isKeyguardLocked()) {
             mWindowManager.deferSurfaceLayout();
             try {
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController()
+                mRootActivityContainer.getDefaultDisplay().getWindowContainerController()
                         .prepareAppTransition(resolveOccludeTransit(),
                                 false /* alwaysKeepCurrent */, 0 /* flags */,
                                 true /* forceOverride */);
                 updateKeyguardSleepToken();
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.executeAppTransition();
             } finally {
                 mWindowManager.continueSurfaceLayout();
@@ -319,21 +323,23 @@
         // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
         // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
         // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
-        if (mWindowManager.isKeyguardSecure()) {
-            mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
-            mDismissalRequested = true;
+        if (!mWindowManager.isKeyguardSecure()) {
+            return;
+        }
 
-            // If we are about to unocclude the Keyguard, but we can dismiss it without security,
-            // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
-            final DisplayWindowController dwc =
-                    mStackSupervisor.getDefaultDisplay().getWindowContainerController();
-            if (mKeyguardShowing && canDismissKeyguard()
-                    && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
-                dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
-                        0 /* flags */, true /* forceOverride */);
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.executeAppTransition();
-            }
+        mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
+        mDismissalRequested = true;
+
+        // If we are about to unocclude the Keyguard, but we can dismiss it without security,
+        // we immediately dismiss the Keyguard so the activity gets shown without a flicker.
+        final DisplayWindowController dwc =
+                mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
+        if (mKeyguardShowing && canDismissKeyguard()
+                && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+            dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */,
+                    0 /* flags */, true /* forceOverride */);
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+            mWindowManager.executeAppTransition();
         }
     }
 
@@ -350,7 +356,7 @@
 
     private int resolveOccludeTransit() {
         final DisplayWindowController dwc =
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+                mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
                 && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
                 // TODO(b/113840485): Handle app transition for individual display.
@@ -377,7 +383,8 @@
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
-            final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+            final ActivityStack stack =
+                    mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
             if (stack == null) {
                 return;
             }
@@ -387,8 +394,9 @@
     }
 
     private void updateKeyguardSleepToken() {
-        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
-            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+        for (int displayNdx = mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx);
             final KeyguardDisplayState state = getDisplay(display.mDisplayId);
             if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
                 state.acquiredSleepToken();
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
new file mode 100644
index 0000000..93e2d8d
--- /dev/null
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+
+/**
+ * Multi-cast implementation of {@link ActivityMetricsLaunchObserver}.
+ *
+ * <br /><br />
+ * If this class is called through the {@link ActivityMetricsLaunchObserver} interface,
+ * then the call is forwarded to all registered observers at the time.
+ *
+ * <br /><br />
+ * All calls are invoked asynchronously in-order on a background thread. This fulfills the
+ * sequential ordering guarantee in {@link ActivityMetricsLaunchObserverRegistry}.
+ *
+ * @see ActivityTaskManagerInternal#getLaunchObserverRegistry()
+ */
+class LaunchObserverRegistryImpl implements
+        ActivityMetricsLaunchObserverRegistry, ActivityMetricsLaunchObserver {
+    private final ArrayList<ActivityMetricsLaunchObserver> mList = new ArrayList<>();
+
+    /**
+     * All calls are posted to a handler because:
+     *
+     * 1. We don't know how long the observer will take to handle this call and we don't want
+     *    to block the WM critical section on it.
+     * 2. We don't know the lock ordering of the observer so we don't want to expose a chance
+     *    of deadlock.
+     */
+    private final Handler mHandler;
+
+    public LaunchObserverRegistryImpl(Looper looper) {
+        mHandler = new Handler(looper);
+    }
+
+    @Override
+    public void registerLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleRegisterLaunchObserver, this, launchObserver));
+    }
+
+    @Override
+    public void unregisterLaunchObserver(ActivityMetricsLaunchObserver launchObserver) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleUnregisterLaunchObserver, this, launchObserver));
+    }
+
+    @Override
+    public void onIntentStarted(Intent intent) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+    }
+
+    @Override
+    public void onIntentFailed() {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleOnIntentFailed, this));
+    }
+
+    @Override
+    public void onActivityLaunched(
+            @ActivityRecordProto byte[] activity,
+            int temperature) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleOnActivityLaunched,
+                this, activity, temperature));
+    }
+
+    @Override
+    public void onActivityLaunchCancelled(
+        @ActivityRecordProto byte[] activity) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleOnActivityLaunchCancelled, this, activity));
+    }
+
+    @Override
+    public void onActivityLaunchFinished(
+        @ActivityRecordProto byte[] activity) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+    }
+
+    // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
+    // unbound (i.e. not capture any variables explicitly or implicitly) to fulfill the
+    // singleton-lambda requirement.
+
+    private void handleRegisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+        mList.add(observer);
+    }
+
+    private void handleUnregisterLaunchObserver(ActivityMetricsLaunchObserver observer) {
+        mList.remove(observer);
+    }
+
+    private void handleOnIntentStarted(Intent intent) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+             ActivityMetricsLaunchObserver o = mList.get(i);
+             o.onIntentStarted(intent);
+        }
+    }
+
+    private void handleOnIntentFailed() {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+             ActivityMetricsLaunchObserver o = mList.get(i);
+             o.onIntentFailed();
+        }
+    }
+
+    private void handleOnActivityLaunched(
+            @ActivityRecordProto byte[] activity,
+            @Temperature int temperature) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+             ActivityMetricsLaunchObserver o = mList.get(i);
+             o.onActivityLaunched(activity, temperature);
+        }
+    }
+
+    private void handleOnActivityLaunchCancelled(
+            @ActivityRecordProto byte[] activity) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+             ActivityMetricsLaunchObserver o = mList.get(i);
+             o.onActivityLaunchCancelled(activity);
+        }
+    }
+
+    private void handleOnActivityLaunchFinished(
+            @ActivityRecordProto byte[] activity) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+            ActivityMetricsLaunchObserver o = mList.get(i);
+            o.onActivityLaunchFinished(activity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 72d5143..3062d34 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -223,7 +223,8 @@
     private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) {
         final ActivityStack<?> stack = task.getStack();
         final int displayId = stack.mDisplayId;
-        final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+        final ActivityDisplay display =
+                mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
         final DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
 
@@ -245,8 +246,8 @@
     }
 
     void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams outParams) {
-        final ComponentName name = task != null ? task.realActivity : activity.realActivity;
-        final int userId = task != null ? task.userId : activity.userId;
+        final ComponentName name = task != null ? task.realActivity : activity.mActivityComponent;
+        final int userId = task != null ? task.userId : activity.mUserId;
 
         outParams.reset();
         Map<ComponentName, PersistableLaunchParams> map = mMap.get(userId);
@@ -259,7 +260,7 @@
             return;
         }
 
-        final ActivityDisplay display = mSupervisor.getActivityDisplay(
+        final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(
                 persistableParams.mDisplayUniqueId);
         if (display != null) {
             outParams.mPreferredDisplayId =  display.mDisplayId;
@@ -268,7 +269,7 @@
         outParams.mBounds.set(persistableParams.mBounds);
     }
 
-    private void onPackageRemoved(String packageName) {
+    void removeRecordForPackage(String packageName) {
         final List<File> fileToDelete = new ArrayList<>();
         for (int i = 0; i < mMap.size(); ++i) {
             int userId = mMap.keyAt(i);
@@ -309,7 +310,7 @@
 
         @Override
         public void onPackageRemoved(String packageName) {
-            LaunchParamsPersister.this.onPackageRemoved(packageName);
+            removeRecordForPackage(packageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index b49d304..1a2aa2f 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -186,7 +186,6 @@
                     createSurface();
                 }
                 t.setPosition(mSurface, mSurfaceFrame.left, mSurfaceFrame.top);
-                t.setSize(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
                 t.setWindowCrop(mSurface, mSurfaceFrame.width(), mSurfaceFrame.height());
                 t.show(mSurface);
             } else if (mSurface != null) {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 41d0777..d2f2863 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -225,7 +225,7 @@
      * of the last locked task and finishing it would mean that lock task mode is ended illegally.
      */
     boolean activityBlockedFromFinish(ActivityRecord activity) {
-        final TaskRecord task = activity.getTask();
+        final TaskRecord task = activity.getTaskRecord();
         if (activity == task.getRootActivity()
                 && activity == task.getTopActivity()
                 && task.mLockTaskAuth != LOCK_TASK_AUTH_LAUNCHABLE_PRIV
@@ -447,7 +447,7 @@
             return;
         }
         task.performClearTaskLocked();
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     /**
@@ -579,7 +579,7 @@
         if (andResume) {
             mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
                     lockTaskModeState != LOCK_TASK_MODE_NONE);
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
             final ActivityStack stack = task.getStack();
             if (stack != null) {
                 stack.getDisplay().getWindowContainerController().executeAppTransition();
@@ -641,12 +641,13 @@
             taskChanged = true;
         }
 
-        for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) {
-            mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated();
+        for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1;
+             displayNdx >= 0; --displayNdx) {
+            mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated();
         }
 
-        final ActivityRecord r = mSupervisor.topRunningActivityLocked();
-        final TaskRecord task = (r != null) ? r.getTask() : null;
+        final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity();
+        final TaskRecord task = (r != null) ? r.getTaskRecord() : null;
         if (mLockTaskModeTasks.isEmpty() && task!= null
                 && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
             // This task must have just been authorized.
@@ -657,7 +658,7 @@
         }
 
         if (taskChanged) {
-            mSupervisor.resumeFocusedStacksTopActivitiesLocked();
+            mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java
index 3ef42e7..1c7ebd6 100644
--- a/services/core/java/com/android/server/wm/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java
@@ -41,7 +41,7 @@
     PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
             Rect outBounds) {
         return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
-                mStackSupervisor.mWindowManager);
+                mRootActivityContainer.mWindowManager);
     }
 
     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 476c1f9..1e287b4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -79,7 +79,7 @@
             int callingPid) {
         mService = atm;
         mStackSupervisor = stackSupervisor;
-        mDefaultDisplay = stackSupervisor.getDefaultDisplay();
+        mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay();
         mActivityStartController = activityStartController;
         mWindowManager = wm;
         mCallingPid = callingPid;
@@ -94,7 +94,7 @@
 
         // TODO(multi-display) currently only support recents animation in default display.
         final DisplayWindowController dwc =
-                mStackSupervisor.getDefaultDisplay().getWindowContainerController();
+                mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController();
         if (!mWindowManager.canStartRecentsAnimation()) {
             notifyAnimationCancelBeforeStart(recentsAnimationRunner);
             if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
@@ -124,8 +124,8 @@
         // Send launch hint if we are actually launching the target. If it's already visible
         // (shouldn't happen in general) we don't need to send it.
         if (targetActivity == null || !targetActivity.visible) {
-            mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */,
-                    targetActivity);
+            mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded(
+                    true /* forceSend */, targetActivity);
         }
 
         mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
@@ -148,8 +148,8 @@
                 // If there are multiple tasks in the target stack (ie. the home stack, with 3p
                 // and default launchers coexisting), then move the task to the top as a part of
                 // moving the stack to the front
-                if (targetStack.topTask() != targetActivity.getTask()) {
-                    targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+                if (targetStack.topTask() != targetActivity.getTaskRecord()) {
+                    targetStack.addTask(targetActivity.getTaskRecord(), true /* toTop */,
                             "startRecentsActivity");
                 }
             } else {
@@ -192,7 +192,7 @@
 
             // If we updated the launch-behind state, update the visibility of the activities after
             // we fetch the visible tasks to be controlled by the animation
-            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+            mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
 
             mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
                     targetActivity);
@@ -215,7 +215,8 @@
             @Deprecated IAssistDataReceiver assistDataReceiver, int userId) {
         final AppOpsManager appOpsManager = (AppOpsManager)
                 mService.mContext.getSystemService(Context.APP_OPS_SERVICE);
-        final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities();
+        final List<IBinder> topActivities =
+                mService.mRootActivityContainer.getTopVisibleActivities();
         final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks;
         if (assistDataReceiver != null) {
             assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver,
@@ -283,7 +284,7 @@
             // Just to be sure end the launch hint in case the target activity was never launched.
             // However, if we're keeping the activity and making it visible, we can leave it on.
             if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mStackSupervisor.sendPowerHintForLaunchEndIfNeeded();
+                mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded();
             }
 
             mService.mH.post(
@@ -343,8 +344,8 @@
                     }
 
                     mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
-                    mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                    mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false);
+                    mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
 
                     // No reason to wait for the pausing activity in this case, as the hiding of
                     // surfaces needs to be done immediately.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b6609e4..83ba384 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -504,10 +504,12 @@
     public void binderDied() {
         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
 
-        // Clear associated input consumers on runner death
-        final InputMonitor inputMonitor =
-                mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
-        inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        synchronized (mService.getWindowManagerLock()) {
+            // Clear associated input consumers on runner death
+            final InputMonitor inputMonitor =
+                    mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
+            inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        }
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -603,8 +605,8 @@
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
             final WindowContainer container = mTask.getParent();
-            container.getRelativePosition(mPosition);
-            container.getBounds(mBounds);
+            container.getRelativeDisplayedPosition(mPosition);
+            mBounds.set(container.getDisplayedBounds());
         }
 
         RemoteAnimationTarget createRemoteAnimationApp() {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
new file mode 100644
index 0000000..1d86b46
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -0,0 +1,2297 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
+import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS;
+import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID;
+import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT;
+import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER;
+import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES;
+import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
+import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerService.ANIMATE;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
+
+import static java.lang.Integer.MAX_VALUE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.power.V1_0.PowerHint;
+import android.os.Build;
+import android.os.FactoryTest;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
+import android.util.DisplayMetrics;
+import android.util.IntArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.am.AppTimeTracker;
+import com.android.server.am.UserState;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Root node for activity containers.
+ * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The
+ * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy.
+ */
+class RootActivityContainer extends ConfigurationContainer
+        implements DisplayManager.DisplayListener {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM;
+    static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
+    static final String TAG_STATES = TAG + POSTFIX_STATES;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+
+    /**
+     * The modes which affect which tasks are returned when calling
+     * {@link RootActivityContainer#anyTaskForId(int)}.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            MATCH_TASK_IN_STACKS_ONLY,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
+            MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+    })
+    public @interface AnyTaskForIdMatchTaskMode {}
+    // Match only tasks in the current stacks
+    static final int MATCH_TASK_IN_STACKS_ONLY = 0;
+    // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1;
+    // Match either tasks in the current stacks, or in the recent tasks, restoring it to the
+    // provided stack id
+    static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2;
+
+    ActivityTaskManagerService mService;
+    ActivityStackSupervisor mStackSupervisor;
+    WindowManagerService mWindowManager;
+    DisplayManager mDisplayManager;
+    private DisplayManagerInternal mDisplayManagerInternal;
+    // TODO: Remove after object merge with RootWindowContainer.
+    private RootWindowContainer mRootWindowContainer;
+
+    /**
+     * List of displays which contain activities, sorted by z-order.
+     * The last entry in the list is the topmost.
+     */
+    private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
+
+    /** Reference to default display so we can quickly look it up. */
+    private ActivityDisplay mDefaultDisplay;
+    private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
+
+    /** The current user */
+    int mCurrentUser;
+    /** Stack id of the front stack when user switched, indexed by userId. */
+    SparseIntArray mUserStackInFront = new SparseIntArray(2);
+
+    /**
+     * A list of tokens that cause the top activity to be put to sleep.
+     * They are used by components that may hide and block interaction with underlying
+     * activities.
+     */
+    final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>();
+
+    /** Is dock currently minimized. */
+    boolean mIsDockMinimized;
+
+    /** Set when a power hint has started, but not ended. */
+    private boolean mPowerHintSent;
+
+    // The default minimal size that will be used if the activity doesn't specify its minimal size.
+    // It will be calculated when the default display gets added.
+    int mDefaultMinSizeOfResizeableTaskDp = -1;
+
+    // Whether tasks have moved and we need to rank the tasks before next OOM scoring
+    private boolean mTaskLayersChanged = true;
+
+    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
+    private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+    static class FindTaskResult {
+        ActivityRecord mRecord;
+        boolean mIdealMatch;
+
+        void clear() {
+            mRecord = null;
+            mIdealMatch = false;
+        }
+
+        void setTo(FindTaskResult result) {
+            mRecord = result.mRecord;
+            mIdealMatch = result.mIdealMatch;
+        }
+    }
+
+    RootActivityContainer(ActivityTaskManagerService service) {
+        mService = service;
+        mStackSupervisor = service.mStackSupervisor;
+        mStackSupervisor.mRootActivityContainer = this;
+    }
+
+    @VisibleForTesting
+    void setWindowContainer(RootWindowContainer container) {
+        mRootWindowContainer = container;
+        mRootWindowContainer.setRootActivityContainer(this);
+    }
+
+    void setWindowManager(WindowManagerService wm) {
+        mWindowManager = wm;
+        setWindowContainer(mWindowManager.mRoot);
+        mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
+        mDisplayManager.registerDisplayListener(this, mService.mH);
+        mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+        final Display[] displays = mDisplayManager.getDisplays();
+        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
+            final Display display = displays[displayNdx];
+            final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+            if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+                mDefaultDisplay = activityDisplay;
+            }
+            addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
+        }
+        calculateDefaultMinimalSizeOfResizeableTasks();
+
+        final ActivityDisplay defaultDisplay = getDefaultDisplay();
+
+        defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+    }
+
+    // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
+    ActivityDisplay getDefaultDisplay() {
+        return mDefaultDisplay;
+    }
+
+    /**
+     * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is
+     * defined in {@link DisplayInfo#uniqueId}.
+     *
+     * @param uniqueId the unique ID of the display
+     * @return the {@link ActivityDisplay} or {@code null} if nothing is found.
+     */
+    ActivityDisplay getActivityDisplay(String uniqueId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            final boolean isValid = display.mDisplay.isValid();
+            if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) {
+                return display;
+            }
+        }
+
+        return null;
+    }
+
+    // TODO: Look into consolidating with getActivityDisplayOrCreate()
+    ActivityDisplay getActivityDisplay(int displayId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            if (activityDisplay.mDisplayId == displayId) {
+                return activityDisplay;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get an existing instance of {@link ActivityDisplay} or create new if there is a
+     * corresponding record in display manager.
+     */
+    // TODO: Look into consolidating with getActivityDisplay()
+    ActivityDisplay getActivityDisplayOrCreate(int displayId) {
+        ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+        if (activityDisplay != null) {
+            return activityDisplay;
+        }
+        if (mDisplayManager == null) {
+            // The system isn't fully initialized yet.
+            return null;
+        }
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            // The display is not registered in DisplayManager.
+            return null;
+        }
+        // The display hasn't been added to ActivityManager yet, create a new record now.
+        activityDisplay = new ActivityDisplay(this, display);
+        addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
+        return activityDisplay;
+    }
+
+    /** Check if display with specified id is added to the list. */
+    boolean isDisplayAdded(int displayId) {
+        return getActivityDisplayOrCreate(displayId) != null;
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivity() {
+        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
+    }
+
+    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+        return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+    }
+
+    boolean startHomeOnAllDisplays(int userId, String reason) {
+        boolean homeStarted = false;
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final int displayId = mActivityDisplays.get(i).mDisplayId;
+            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
+        }
+        return homeStarted;
+    }
+
+    /**
+     * This starts home activity on displays that can have system decorations and only if the
+     * home activity can have multiple instances.
+     */
+    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
+        final Intent homeIntent = mService.getHomeIntent();
+        final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent);
+        if (aInfo == null) {
+            return false;
+        }
+
+        if (!canStartHomeOnDisplay(aInfo, displayId,
+                false /* allowInstrumenting */)) {
+            return false;
+        }
+
+        // Update the reason for ANR debugging to verify if the user activity is the one that
+        // actually launched.
+        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
+                aInfo.applicationInfo.uid);
+        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
+                displayId);
+        return true;
+    }
+
+    /**
+     * This resolves the home activity info and updates the home component of the given intent.
+     * @return the home activity info if any.
+     */
+    private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
+        final int flags = ActivityManagerService.STOCK_PM_FLAGS;
+        final ComponentName comp = homeIntent.getComponent();
+        ActivityInfo aInfo = null;
+        try {
+            if (comp != null) {
+                // Factory test.
+                aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);
+            } else {
+                final String resolvedType =
+                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
+                final ResolveInfo info = AppGlobals.getPackageManager()
+                        .resolveIntent(homeIntent, resolvedType, flags, userId);
+                if (info != null) {
+                    aInfo = info.activityInfo;
+                }
+            }
+        } catch (RemoteException e) {
+            // ignore
+        }
+
+        if (aInfo == null) {
+            Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable());
+            return null;
+        }
+
+        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
+        aInfo = new ActivityInfo(aInfo);
+        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
+        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
+        return aInfo;
+    }
+
+    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
+        if (!mService.isBooting() && !mService.isBooted()) {
+            // Not ready yet!
+            return false;
+        }
+
+        if (displayId == INVALID_DISPLAY) {
+            displayId = DEFAULT_DISPLAY;
+        }
+
+        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+        final String myReason = reason + " resumeHomeActivity";
+
+        // Only resume home activity if isn't finishing.
+        if (r != null && !r.finishing) {
+            r.moveFocusableActivityToTop(myReason);
+            return resumeFocusedStacksTopActivities(r.getActivityStack(), prev, null);
+        }
+        return startHomeOnDisplay(mCurrentUser, myReason, displayId);
+    }
+
+    /**
+     * Check if home activity start should be allowed on a display.
+     * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched.
+     * @param displayId The id of the target display.
+     * @param allowInstrumenting Whether launching home should be allowed if being instrumented.
+     * @return {@code true} if allow to launch, {@code false} otherwise.
+     */
+    boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId,
+            boolean allowInstrumenting) {
+        if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
+                && mService.mTopAction == null) {
+            // We are running in factory test mode, but unable to find the factory test app, so
+            // just sit around displaying the error message and don't try to start anything.
+            return false;
+        }
+
+        final WindowProcessController app =
+                mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid);
+        if (!allowInstrumenting && app != null && app.isInstrumenting()) {
+            // Don't do this if the home app is currently being instrumented.
+            return false;
+        }
+
+        if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY
+                && displayId == mService.mVr2dDisplayId)) {
+            // No restrictions to default display or vr 2d display.
+            return true;
+        }
+
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+            // Can't launch home on display that doesn't support system decorations.
+            return false;
+        }
+
+        final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK
+                && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE
+                && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        if (!supportMultipleInstance) {
+            // Can't launch home on other displays if it requested to be single instance. Also we
+            // don't allow home applications that target before Q to have multiple home activity
+            // instances because they may not be expected to have multiple home scenario and
+            // haven't explicitly request for single instance.
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Ensure all activities visibility, update orientation and configuration.
+     *
+     * @param starting The currently starting activity or {@code null} if there is none.
+     * @param displayId The id of the display where operation is executed.
+     * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to
+     *                                  {@code true} if config changed.
+     * @param deferResume Whether to defer resume while updating config.
+     * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched
+     *         because of configuration update.
+     */
+    boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
+            boolean markFrozenIfConfigChanged, boolean deferResume) {
+        // First ensure visibility without updating the config just yet. We need this to know what
+        // activities are affecting configuration now.
+        // Passing null here for 'starting' param value, so that visibility of actual starting
+        // activity will be properly updated.
+        ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */, false /* notifyClients */);
+
+        if (displayId == INVALID_DISPLAY) {
+            // The caller didn't provide a valid display id, skip updating config.
+            return true;
+        }
+
+        // Force-update the orientation from the WindowManager, since we need the true configuration
+        // to send to the client now.
+        final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+                getDisplayOverrideConfiguration(displayId),
+                starting != null && starting.mayFreezeScreenLocked(starting.app)
+                        ? starting.appToken : null,
+                displayId, true /* forceUpdate */);
+        if (starting != null && markFrozenIfConfigChanged && config != null) {
+            starting.frozenBeforeDestroy = true;
+        }
+
+        // Update the configuration of the activities on the display.
+        return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+                displayId);
+    }
+
+    /**
+     * @return a list of activities which are the top ones in each visible stack. The first
+     * entry will be the focused activity.
+     */
+    List<IBinder> getTopVisibleActivities() {
+        final ArrayList<IBinder> topActivityTokens = new ArrayList<>();
+        final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
+        // Traverse all displays.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            // Traverse all stacks on a display.
+            for (int j = display.getChildCount() - 1; j >= 0; --j) {
+                final ActivityStack stack = display.getChildAt(j);
+                // Get top activity from a visible stack and add it to the list.
+                if (stack.shouldBeVisible(null /* starting */)) {
+                    final ActivityRecord top = stack.getTopActivity();
+                    if (top != null) {
+                        if (stack == topFocusedStack) {
+                            topActivityTokens.add(0, top.appToken);
+                        } else {
+                            topActivityTokens.add(top.appToken);
+                        }
+                    }
+                }
+            }
+        }
+        return topActivityTokens;
+    }
+
+    ActivityStack getTopDisplayFocusedStack() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
+            if (focusedStack != null) {
+                return focusedStack;
+            }
+        }
+        return null;
+    }
+
+    ActivityRecord getTopResumedActivity() {
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack == null) {
+            return null;
+        }
+        final ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+        if (resumedActivity != null && resumedActivity.app != null) {
+            return resumedActivity;
+        }
+        // The top focused stack might not have a resumed activity yet - look on all displays in
+        // focus order.
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+            if (resumedActivityOnDisplay != null) {
+                return resumedActivityOnDisplay;
+            }
+        }
+        return null;
+    }
+
+    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+            return false;
+        }
+
+        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+    }
+
+    boolean isTopDisplayFocusedStack(ActivityStack stack) {
+        return stack != null && stack == getTopDisplayFocusedStack();
+    }
+
+    void updatePreviousProcess(ActivityRecord r) {
+        // Now that this process has stopped, we may want to consider it to be the previous app to
+        // try to keep around in case the user wants to return to it.
+
+        // First, found out what is currently the foreground app, so that we don't blow away the
+        // previous app if this activity is being hosted by the process that is actually still the
+        // foreground.
+        WindowProcessController fgApp = null;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (isTopDisplayFocusedStack(stack)) {
+                    final ActivityRecord resumedActivity = stack.getResumedActivity();
+                    if (resumedActivity != null) {
+                        fgApp = resumedActivity.app;
+                    } else if (stack.mPausingActivity != null) {
+                        fgApp = stack.mPausingActivity.app;
+                    }
+                    break;
+                }
+            }
+        }
+
+        // Now set this one as the previous process, only if that really makes sense to.
+        if (r.hasProcess() && fgApp != null && r.app != fgApp
+                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+                && r.app != mService.mHomeProcess) {
+            mService.mPreviousProcess = r.app;
+            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+        }
+    }
+
+    boolean attachApplication(WindowProcessController app) throws RemoteException {
+        final String processName = app.mName;
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack != null) {
+                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+                final ActivityRecord top = stack.topRunningActivityLocked();
+                final int size = mTmpActivityList.size();
+                for (int i = 0; i < size; i++) {
+                    final ActivityRecord activity = mTmpActivityList.get(i);
+                    if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
+                            && processName.equals(activity.processName)) {
+                        try {
+                            if (mStackSupervisor.realStartActivityLocked(activity, app,
+                                    top == activity /* andResume */, true /* checkConfig */)) {
+                                didSomething = true;
+                            }
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Exception in new application when starting activity "
+                                    + top.intent.getComponent().flattenToShortString(), e);
+                            throw e;
+                        }
+                    }
+                }
+            }
+        }
+        if (!didSomething) {
+            ensureActivitiesVisible(null, 0, false /* preserve_windows */);
+        }
+        return didSomething;
+    }
+
+    /**
+     * Make sure that all activities that need to be visible in the system actually are and update
+     * their configuration.
+     */
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows) {
+        ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
+    }
+
+    /**
+     * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
+     */
+    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+            boolean preserveWindows, boolean notifyClients) {
+        mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate();
+        try {
+            // First the front stacks. In case any are not fullscreen and are in front of home.
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
+                            notifyClients);
+                }
+            }
+        } finally {
+            mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate();
+        }
+    }
+
+    boolean switchUser(int userId, UserState uss) {
+        final int focusStackId = getTopDisplayFocusedStack().getStackId();
+        // We dismiss the docked stack whenever we switch users.
+        final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack();
+        if (dockedStack != null) {
+            mStackSupervisor.moveTasksToFullscreenStackLocked(
+                    dockedStack, dockedStack.isFocusedStackOnDisplay());
+        }
+        // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
+        // also cause all tasks to be moved to the fullscreen stack at a position that is
+        // appropriate.
+        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+        mUserStackInFront.put(mCurrentUser, focusStackId);
+        final int restoreStackId =
+                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
+        mCurrentUser = userId;
+
+        mStackSupervisor.mStartingUsers.add(uss);
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.switchUserLocked(userId);
+                TaskRecord task = stack.topTask();
+                if (task != null) {
+                    stack.positionChildWindowContainerAtTop(task);
+                }
+            }
+        }
+
+        ActivityStack stack = getStack(restoreStackId);
+        if (stack == null) {
+            stack = getDefaultDisplay().getHomeStack();
+        }
+        final boolean homeInFront = stack.isActivityTypeHome();
+        if (stack.isOnHomeDisplay()) {
+            stack.moveToFront("switchUserOnHomeDisplay");
+        } else {
+            // Stack was moved to another display while user was swapped out.
+            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
+        }
+        return homeInFront;
+    }
+
+    void removeUser(int userId) {
+        mUserStackInFront.delete(userId);
+    }
+
+    /**
+     * Update the last used stack id for non-current user (current user's last
+     * used stack is the focused stack)
+     */
+    void updateUserStack(int userId, ActivityStack stack) {
+        if (userId != mCurrentUser) {
+            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+                    : getDefaultDisplay().getHomeStack().mStackId);
+        }
+    }
+
+    void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+            boolean deferResume) {
+
+        if (stack.inSplitScreenPrimaryWindowingMode()) {
+            mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds,
+                    tempTaskInsetBounds, null, null, preserveWindows, deferResume);
+            return;
+        }
+
+        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
+        if (!allowResizeInDockedMode
+                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
+            // If the docked stack exists, don't resize non-floating stacks independently of the
+            // size computed from the docked stack size (otherwise they will be out of sync)
+            return;
+        }
+
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
+        mWindowManager.deferSurfaceLayout();
+        try {
+            if (stack.affectedBySplitScreenResize()) {
+                if (bounds == null && stack.inSplitScreenWindowingMode()) {
+                    // null bounds = fullscreen windowing mode...at least for now.
+                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                } else if (splitScreenActive) {
+                    // If we are in split-screen mode and this stack support split-screen, then
+                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
+                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                }
+            }
+            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
+            if (!deferResume) {
+                stack.ensureVisibleActivitiesConfigurationLocked(
+                        stack.topRunningActivityLocked(), preserveWindows);
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    /**
+     * Move stack with all its existing content to specified display.
+     * @param stackId Id of stack to move.
+     * @param displayId Id of display to move stack to.
+     * @param onTop Indicates whether container should be place on top or on bottom.
+     */
+    void moveStackToDisplay(int stackId, int displayId, boolean onTop) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId="
+                    + displayId);
+        }
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId="
+                    + stackId);
+        }
+
+        final ActivityDisplay currentDisplay = stack.getDisplay();
+        if (currentDisplay == null) {
+            throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack
+                    + " is not attached to any display.");
+        }
+
+        if (currentDisplay.mDisplayId == displayId) {
+            throw new IllegalArgumentException("Trying to move stack=" + stack
+                    + " to its current displayId=" + displayId);
+        }
+
+        stack.reparent(activityDisplay, onTop, false /* displayRemoved */);
+        // TODO(multi-display): resize stacks properly if moved from split-screen.
+    }
+
+    boolean moveTopStackActivityToPinnedStack(int stackId) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            throw new IllegalArgumentException(
+                    "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+        }
+
+        final ActivityRecord r = stack.topRunningActivityLocked();
+        if (r == null) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
+                    + " in stack=" + stack);
+            return false;
+        }
+
+        if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
+            Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
+                    + " r=" + r);
+            return false;
+        }
+
+        moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */,
+                "moveTopActivityToPinnedStack");
+        return true;
+    }
+
+    void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
+            String reason) {
+
+        mWindowManager.deferSurfaceLayout();
+
+        final ActivityDisplay display = r.getActivityStack().getDisplay();
+        PinnedActivityStack stack = display.getPinnedStack();
+
+        // This will clear the pinned stack by moving an existing task to the full screen stack,
+        // ensuring only one task is present.
+        if (stack != null) {
+            mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP);
+        }
+
+        // Need to make sure the pinned stack exist so we can resize it below...
+        stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP);
+
+        // Calculate the target bounds here before the task is reparented back into pinned windowing
+        // mode (which will reset the saved bounds)
+        final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio);
+
+        try {
+            final TaskRecord task = r.getTaskRecord();
+            // Resize the pinned stack to match the current size of the task the activity we are
+            // going to be moving is currently contained in. We do this to have the right starting
+            // animation bounds for the pinned stack to the desired bounds the caller wants.
+            resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
+
+            if (task.mActivities.size() == 1) {
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
+                        false /* schedulePictureInPictureModeChange */, reason);
+            } else {
+                // There are multiple activities in the task and moving the top activity should
+                // 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(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info,
+                        r.intent, null, null, true);
+                r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
+
+                // Defer resume until below, and do not schedule PiP changes until we animate below
+                newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE,
+                        DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason);
+            }
+
+            // Reset the state that indicates it can enter PiP while pausing after we've moved it
+            // to the pinned stack
+            r.supportsEnterPipOnTaskSwitch = false;
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+
+        stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+                true /* fromFullscreen */);
+
+        // Update the visibility of all activities after the they have been reparented to the new
+        // stack.  This MUST run after the animation above is scheduled to ensure that the windows
+        // drawn signal is scheduled after the bounds animation start call on the bounds animator
+        // thread.
+        ensureActivitiesVisible(null, 0, false /* preserveWindows */);
+        resumeFocusedStacksTopActivities();
+
+        mService.getTaskChangeNotificationController().notifyActivityPinned(r);
+    }
+
+    void executeAppTransitionForAllDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            display.getWindowContainerController().executeAppTransition();
+        }
+    }
+
+    void setDockedStackMinimized(boolean minimized) {
+        // Get currently focused stack before setting mIsDockMinimized. We do this because if
+        // split-screen is active, primary stack will not be focusable (see #isFocusable) while
+        // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null.
+        final ActivityStack current = getTopDisplayFocusedStack();
+        mIsDockMinimized = minimized;
+        if (mIsDockMinimized) {
+            if (current.inSplitScreenPrimaryWindowingMode()) {
+                // The primary split-screen stack can't be focused while it is minimize, so move
+                // focus to something else.
+                current.adjustFocusToNextFocusableStack("setDockedStackMinimized");
+            }
+        }
+    }
+
+    ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) {
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
+        mTmpFindTaskResult.clear();
+
+        // Looking up task on preferred display first
+        final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+        if (preferredDisplay != null) {
+            preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.mDisplayId == preferredDisplayId) {
+                continue;
+            }
+
+            display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+            if (mTmpFindTaskResult.mIdealMatch) {
+                return mTmpFindTaskResult.mRecord;
+            }
+        }
+
+        if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+        return mTmpFindTaskResult.mRecord;
+    }
+
+    /**
+     * Finish the topmost activities in all stacks that belong to the crashed app.
+     * @param app The app that crashed.
+     * @param reason Reason to perform this action.
+     * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished.
+     */
+    int finishTopCrashedActivities(WindowProcessController app, String reason) {
+        TaskRecord finishedTask = null;
+        ActivityStack focusedStack = getTopDisplayFocusedStack();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            // It is possible that request to finish activity might also remove its task and stack,
+            // so we need to be careful with indexes in the loop and check child count every time.
+            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason);
+                if (stack == focusedStack || finishedTask == null) {
+                    finishedTask = t;
+                }
+            }
+        }
+        return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID;
+    }
+
+    boolean resumeFocusedStacksTopActivities() {
+        return resumeFocusedStacksTopActivities(null, null, null);
+    }
+
+    boolean resumeFocusedStacksTopActivities(
+            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+        if (!mStackSupervisor.readyToResume()) {
+            return false;
+        }
+
+        if (targetStack != null && (targetStack.isTopStackOnDisplay()
+                || getTopDisplayFocusedStack() == targetStack)) {
+            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
+        }
+
+        // Resume all top activities in focused stacks on all displays.
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack == null) {
+                continue;
+            }
+            final ActivityRecord r = focusedStack.topRunningActivityLocked();
+            if (r == null || !r.isState(RESUMED)) {
+                focusedStack.resumeTopActivityUncheckedLocked(null, null);
+            } else if (r.isState(RESUMED)) {
+                // Kick off any lingering app transitions form the MoveTaskToFront operation.
+                focusedStack.executeAppTransition(targetOptions);
+            }
+        }
+
+        return false;
+    }
+
+    void applySleepTokens(boolean applyToStacks) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // Set the sleeping state of the display.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final boolean displayShouldSleep = display.shouldSleep();
+            if (displayShouldSleep == display.isSleeping()) {
+                continue;
+            }
+            display.setIsSleeping(displayShouldSleep);
+
+            if (!applyToStacks) {
+                continue;
+            }
+
+            // Set the sleeping state of the stacks on the display.
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (displayShouldSleep) {
+                    stack.goToSleepIfPossible(false /* shuttingDown */);
+                } else {
+                    stack.awakeFromSleepingLocked();
+                    if (stack.isFocusedStackOnDisplay()
+                            && !mStackSupervisor.getKeyguardController()
+                            .isKeyguardOrAodShowing(display.mDisplayId)) {
+                        // If the keyguard is unlocked - resume immediately.
+                        // It is possible that the display will not be awake at the time we
+                        // process the keyguard going away, which can happen before the sleep token
+                        // is released. As a result, it is important we resume the activity here.
+                        resumeFocusedStacksTopActivities();
+                    }
+                }
+            }
+
+            if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) {
+                continue;
+            }
+            // The display is awake now, so clean up the going to sleep list.
+            for (Iterator<ActivityRecord> it =
+                 mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) {
+                final ActivityRecord r = it.next();
+                if (r.getDisplayId() == display.mDisplayId) {
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    protected <T extends ActivityStack> T getStack(int stackId) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(stackId);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    /** @see ActivityDisplay#getStack(int, int) */
+    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
+        final int displayId = stack.mDisplayId;
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        ActivityManager.StackInfo info = new ActivityManager.StackInfo();
+        stack.getWindowContainerBounds(info.bounds);
+        info.displayId = displayId;
+        info.stackId = stack.mStackId;
+        info.userId = stack.mCurrentUser;
+        info.visible = stack.shouldBeVisible(null);
+        // A stack might be not attached to a display.
+        info.position = display != null ? display.getIndexOf(stack) : 0;
+        info.configuration.setTo(stack.getConfiguration());
+
+        ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        final int numTasks = tasks.size();
+        int[] taskIds = new int[numTasks];
+        String[] taskNames = new String[numTasks];
+        Rect[] taskBounds = new Rect[numTasks];
+        int[] taskUserIds = new int[numTasks];
+        for (int i = 0; i < numTasks; ++i) {
+            final TaskRecord task = tasks.get(i);
+            taskIds[i] = task.taskId;
+            taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString()
+                    : task.realActivity != null ? task.realActivity.flattenToString()
+                    : task.getTopActivity() != null ? task.getTopActivity().packageName
+                    : "unknown";
+            taskBounds[i] = new Rect();
+            task.getWindowContainerBounds(taskBounds[i]);
+            taskUserIds[i] = task.userId;
+        }
+        info.taskIds = taskIds;
+        info.taskNames = taskNames;
+        info.taskBounds = taskBounds;
+        info.taskUserIds = taskUserIds;
+
+        final ActivityRecord top = stack.topRunningActivityLocked();
+        info.topActivity = top != null ? top.intent.getComponent() : null;
+        return info;
+    }
+
+    ActivityManager.StackInfo getStackInfo(int stackId) {
+        ActivityStack stack = getStack(stackId);
+        if (stack != null) {
+            return getStackInfo(stack);
+        }
+        return null;
+    }
+
+    ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+        final ActivityStack stack = getStack(windowingMode, activityType);
+        return (stack != null) ? getStackInfo(stack) : null;
+    }
+
+    ArrayList<ActivityManager.StackInfo> getAllStackInfos() {
+        ArrayList<ActivityManager.StackInfo> list = new ArrayList<>();
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                list.add(getStackInfo(stack));
+            }
+        }
+        return list;
+    }
+
+    void deferUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.deferUpdateBounds();
+        }
+    }
+
+    void continueUpdateBounds(int activityType) {
+        final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+        if (stack != null) {
+            stack.continueUpdateBounds();
+        }
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            getActivityDisplayOrCreate(displayId);
+            // Do not start home before booting, or it may accidentally finish booting before it
+            // starts. Instead, we expect home activities to be launched when the system is ready
+            // (ActivityManagerService#systemReady).
+            if (mService.isBooted() || mService.isBooting()) {
+                startHomeOnDisplay(mCurrentUser, "displayAdded", displayId);
+            }
+        }
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId);
+        if (displayId == DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can't remove the primary display.");
+        }
+
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay == null) {
+                return;
+            }
+
+            activityDisplay.remove();
+        }
+    }
+
+    @Override
+    public void onDisplayChanged(int displayId) {
+        if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId);
+        synchronized (mService.mGlobalLock) {
+            final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
+            if (activityDisplay != null) {
+                activityDisplay.onDisplayChanged();
+            }
+        }
+    }
+
+    /** Update lists of UIDs that are present on displays and have access to them. */
+    void updateUIDsPresentOnDisplay() {
+        mDisplayAccessUIDs.clear();
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            // Only bother calculating the whitelist for private displays
+            if (activityDisplay.isPrivate()) {
+                mDisplayAccessUIDs.append(
+                        activityDisplay.mDisplayId, activityDisplay.getPresentUIDs());
+            }
+        }
+        // Store updated lists in DisplayManager. Callers from outside of AM should get them there.
+        mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs);
+    }
+
+    ActivityStack findStackBehind(ActivityStack stack) {
+        final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+        if (display != null) {
+            for (int i = display.getChildCount() - 1; i >= 0; i--) {
+                if (display.getChildAt(i) == stack && i > 0) {
+                    return display.getChildAt(i - 1);
+                }
+            }
+        }
+        throw new IllegalStateException("Failed to find a stack behind stack=" + stack
+                + " in=" + display);
+    }
+
+    @Override
+    protected int getChildCount() {
+        return mActivityDisplays.size();
+    }
+
+    @Override
+    protected ActivityDisplay getChildAt(int index) {
+        return mActivityDisplays.get(index);
+    }
+
+    @Override
+    protected ConfigurationContainer getParent() {
+        return null;
+    }
+
+    // TODO: remove after object merge with RootWindowContainer
+    void onChildPositionChanged(DisplayWindowController childController, int position) {
+        // Assume AM lock is held from positionChildAt of controller in each hierarchy.
+        final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
+        if (display != null) {
+            positionChildAt(display, position);
+        }
+    }
+
+    /** Change the z-order of the given display. */
+    private void positionChildAt(ActivityDisplay display, int position) {
+        if (position >= mActivityDisplays.size()) {
+            position = mActivityDisplays.size() - 1;
+        } else if (position < 0) {
+            position = 0;
+        }
+
+        if (mActivityDisplays.isEmpty()) {
+            mActivityDisplays.add(display);
+        } else if (mActivityDisplays.get(position) != display) {
+            mActivityDisplays.remove(display);
+            mActivityDisplays.add(position, display);
+        }
+    }
+
+    @VisibleForTesting
+    void addChild(ActivityDisplay activityDisplay, int position) {
+        positionChildAt(activityDisplay, position);
+        mRootWindowContainer.positionChildAt(position,
+                activityDisplay.getWindowContainerController().mContainer);
+    }
+
+    void removeChild(ActivityDisplay activityDisplay) {
+        // The caller must tell the controller of {@link ActivityDisplay} to release its container
+        // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+        mActivityDisplays.remove(activityDisplay);
+    }
+
+    Configuration getDisplayOverrideConfiguration(int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        return activityDisplay.getOverrideConfiguration();
+    }
+
+    void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException("No display found with id: " + displayId);
+        }
+
+        activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
+    }
+
+    void prepareForShutdown() {
+        for (int i = 0; i < mActivityDisplays.size(); i++) {
+            createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId);
+        }
+    }
+
+    ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) {
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException("Invalid display: " + displayId);
+        }
+
+        final SleepTokenImpl token = new SleepTokenImpl(tag, displayId);
+        mSleepTokens.add(token);
+        display.mAllSleepTokens.add(token);
+        return token;
+    }
+
+    private void removeSleepToken(SleepTokenImpl token) {
+        mSleepTokens.remove(token);
+
+        final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
+        if (display != null) {
+            display.mAllSleepTokens.remove(token);
+            if (display.mAllSleepTokens.isEmpty()) {
+                mService.updateSleepIfNeededLocked();
+            }
+        }
+    }
+
+    void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.addStartingWindowsForVisibleActivities(taskSwitch);
+            }
+        }
+    }
+
+    void invalidateTaskLayers() {
+        mTaskLayersChanged = true;
+    }
+
+    void rankTaskLayersIfNeeded() {
+        if (!mTaskLayersChanged) {
+            return;
+        }
+        mTaskLayersChanged = false;
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            int baseLayer = 0;
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                baseLayer += stack.rankTaskLayers(baseLayer);
+            }
+        }
+    }
+
+    void clearOtherAppTimeTrackers(AppTimeTracker except) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.clearOtherAppTimeTrackers(except);
+            }
+        }
+    }
+
+    void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.scheduleDestroyActivities(app, reason);
+            }
+        }
+    }
+
+    void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
+        // Tasks is non-null only if two or more tasks are found.
+        ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks();
+        if (tasks == null) {
+            if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
+            return;
+        }
+        // If we have activities in multiple tasks that are in a position to be destroyed,
+        // let's iterate through the tasks and release the oldest one.
+        final int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int stackCount = display.getChildCount();
+            // Step through all stacks starting from behind, to hit the oldest things first.
+            for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                // Try to release activities in this stack; if we manage to, we are done.
+                if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
+                    return;
+                }
+            }
+        }
+    }
+
+    // Tries to put all activity stacks to sleep. Returns true if all stacks were
+    // successfully put to sleep.
+    boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
+        boolean allSleep = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (allowDelay) {
+                    allSleep &= stack.goToSleepIfPossible(shuttingDown);
+                } else {
+                    stack.goToSleep();
+                }
+            }
+        }
+        return allSleep;
+    }
+
+    void handleAppCrash(WindowProcessController app) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.handleAppCrash(app);
+            }
+        }
+    }
+
+    ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord ar = stack.findActivityLocked(
+                        intent, info, compareIntentFilters);
+                if (ar != null) {
+                    return ar;
+                }
+            }
+        }
+        return null;
+    }
+
+    boolean hasAwakeDisplay() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (!display.shouldSleep()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) {
+        return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */);
+    }
+
+    /**
+     * Returns the right stack to use for launching factoring in all the input parameters.
+     *
+     * @param r The activity we are trying to launch. Can be null.
+     * @param options The activity options used to the launch. Can be null.
+     * @param candidateTask The possible task the activity might be launched in. Can be null.
+     * @params launchParams The resolved launch params to use.
+     *
+     * @return The stack to use for the launch or INVALID_STACK_ID.
+     */
+    <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r,
+            @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        int taskId = INVALID_TASK_ID;
+        int displayId = INVALID_DISPLAY;
+        //Rect bounds = null;
+
+        // We give preference to the launch preference in activity options.
+        if (options != null) {
+            taskId = options.getLaunchTaskId();
+            displayId = options.getLaunchDisplayId();
+        }
+
+        // First preference for stack goes to the task Id set in the activity options. Use the stack
+        // associated with that if possible.
+        if (taskId != INVALID_TASK_ID) {
+            // Temporarily set the task id to invalid in case in re-entry.
+            options.setLaunchTaskId(INVALID_TASK_ID);
+            final TaskRecord task = anyTaskForId(taskId,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
+            options.setLaunchTaskId(taskId);
+            if (task != null) {
+                return task.getStack();
+            }
+        }
+
+        final int activityType = resolveActivityType(r, options, candidateTask);
+        T stack;
+
+        // Next preference for stack goes to the display Id set the candidate display.
+        if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
+            displayId = launchParams.mPreferredDisplayId;
+        }
+        if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) {
+            if (r != null) {
+                stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options,
+                        launchParams);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+            final ActivityDisplay display = getActivityDisplayOrCreate(displayId);
+            if (display != null) {
+                stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+                if (stack != null) {
+                    return stack;
+                }
+            }
+        }
+
+        // Give preference to the stack and display of the input task and activity if they match the
+        // mode we want to launch into.
+        stack = null;
+        ActivityDisplay display = null;
+        if (candidateTask != null) {
+            stack = candidateTask.getStack();
+        }
+        if (stack == null && r != null) {
+            stack = r.getActivityStack();
+        }
+        if (stack != null) {
+            display = stack.getDisplay();
+            if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
+                int windowingMode = launchParams != null ? launchParams.mWindowingMode
+                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+                if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                    windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+                            activityType);
+                }
+                if (stack.isCompatible(windowingMode, activityType)) {
+                    return stack;
+                }
+                if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
+                        && display.getSplitScreenPrimaryStack() == stack
+                        && candidateTask == stack.topTask()) {
+                    // This is a special case when we try to launch an activity that is currently on
+                    // top of split-screen primary stack, but is targeting split-screen secondary.
+                    // In this case we don't want to move it to another stack.
+                    // TODO(b/78788972): Remove after differentiating between preferred and required
+                    // launch options.
+                    return stack;
+                }
+            }
+        }
+
+        if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
+            display = getDefaultDisplay();
+        }
+
+        return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+    }
+
+    /** @return true if activity record is null or can be launched on provided display. */
+    private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) {
+        if (r == null) {
+            return true;
+        }
+        return r.canBeLaunchedOnDisplay(displayId);
+    }
+
+    /**
+     * Get a topmost stack on the display, that is a valid launch stack for specified activity.
+     * If there is no such stack, new dynamic stack can be created.
+     * @param displayId Target display.
+     * @param r Activity that should be launched there.
+     * @param candidateTask The possible task the activity might be put in.
+     * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
+     */
+    private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId);
+        if (activityDisplay == null) {
+            throw new IllegalArgumentException(
+                    "Display with displayId=" + displayId + " not found.");
+        }
+
+        if (!r.canBeLaunchedOnDisplay(displayId)) {
+            return null;
+        }
+
+        // If {@code r} is already in target display and its task is the same as the candidate task,
+        // the intention should be getting a launch stack for the reusable activity, so we can use
+        // the existing stack.
+        if (r.getDisplayId() == displayId && r.getTaskRecord() == candidateTask) {
+            return candidateTask.getStack();
+        }
+
+        // Return the topmost valid stack on the display.
+        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = activityDisplay.getChildAt(i);
+            if (isValidLaunchStack(stack, r)) {
+                return stack;
+            }
+        }
+
+        // If there is no valid stack on the external display - check if new dynamic stack will do.
+        if (displayId != DEFAULT_DISPLAY) {
+            final int windowingMode;
+            if (launchParams != null) {
+                // When launch params is not null, we always defer to its windowing mode. Sometimes
+                // it could be unspecified, which indicates it should inherit windowing mode from
+                // display.
+                windowingMode = launchParams.mWindowingMode;
+            } else {
+                windowingMode = options != null ? options.getLaunchWindowingMode()
+                        : r.getWindowingMode();
+            }
+            final int activityType =
+                    options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED
+                            ? options.getLaunchActivityType() : r.getActivityType();
+            return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/);
+        }
+
+        Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId);
+        return null;
+    }
+
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+            @Nullable ActivityOptions options,
+            @Nullable LaunchParamsController.LaunchParams launchParams) {
+        return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options,
+                launchParams);
+    }
+
+    // TODO: Can probably be consolidated into getLaunchStack()...
+    private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) {
+        switch (stack.getActivityType()) {
+            case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
+            case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
+            case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+        }
+        // There is a 1-to-1 relationship between stack and task when not in
+        // primary split-windowing mode.
+        if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            return false;
+        } else {
+            return r.supportsSplitScreenWindowingMode();
+        }
+    }
+
+    int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+            @Nullable TaskRecord task) {
+        // Preference is given to the activity type for the activity then the task since the type
+        // once set shouldn't change.
+        int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
+        if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
+            activityType = task.getActivityType();
+        }
+        if (activityType != ACTIVITY_TYPE_UNDEFINED) {
+            return activityType;
+        }
+        if (options != null) {
+            activityType = options.getLaunchActivityType();
+        }
+        return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
+    }
+
+    /**
+     * Get next focusable stack in the system. This will search through the stack on the same
+     * display as the current focused stack, looking for a focusable and visible stack, different
+     * from the target stack. If no valid candidates will be found, it will then go through all
+     * displays and stacks in last-focused order.
+     *
+     * @param currentFocus The stack that previously had focus.
+     * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
+     *                     candidate.
+     * @return Next focusable {@link ActivityStack}, {@code null} if not found.
+     */
+    ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus,
+            boolean ignoreCurrent) {
+        // First look for next focusable stack on the same display
+        final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+        final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+                currentFocus, ignoreCurrent);
+        if (preferredFocusableStack != null) {
+            return preferredFocusableStack;
+        }
+        if (preferredDisplay.supportsSystemDecorations()) {
+            // Stop looking for focusable stack on other displays because the preferred display
+            // supports system decorations. Home activity would be launched on the same display if
+            // no focusable stack found.
+            return null;
+        }
+
+        // Now look through all displays
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display == preferredDisplay) {
+                // We've already checked this one
+                continue;
+            }
+            final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+                    ignoreCurrent);
+            if (nextFocusableStack != null) {
+                return nextFocusableStack;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get next valid stack for launching provided activity in the system. This will search across
+     * displays and stacks in last-focused order for a focusable and visible stack, except those
+     * that are on a currently focused display.
+     *
+     * @param r The activity that is being launched.
+     * @param currentFocus The display that previously had focus and thus needs to be ignored when
+     *                     searching for the next candidate.
+     * @return Next valid {@link ActivityStack}, null if not found.
+     */
+    ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display.mDisplayId == currentFocus) {
+                continue;
+            }
+            final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
+                    null /* options */, null /* launchParams */);
+            if (stack != null) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
+    boolean handleAppDied(WindowProcessController app) {
+        boolean hasVisibleActivities = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                hasVisibleActivities |= stack.handleAppDiedLocked(app);
+            }
+        }
+        return hasVisibleActivities;
+    }
+
+    void closeSystemDialogs() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.closeSystemDialogsLocked();
+            }
+        }
+    }
+
+    /** @return true if some activity was finished (or would have finished if doit were true). */
+    boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
+            boolean doit, boolean evenPersistent, int userId) {
+        boolean didSomething = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                if (stack.finishDisabledPackageActivitiesLocked(
+                        packageName, filterByClasses, doit, evenPersistent, userId)) {
+                    didSomething = true;
+                }
+            }
+        }
+        return didSomething;
+    }
+
+    void updateActivityApplicationInfo(ApplicationInfo aInfo) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.updateActivityApplicationInfoLocked(aInfo);
+            }
+        }
+    }
+
+    void finishVoiceTask(IVoiceInteractionSession session) {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            final int numStacks = display.getChildCount();
+            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.finishVoiceTask(session);
+            }
+        }
+    }
+
+    /**
+     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+     */
+    void removeStacksInWindowingModes(int... windowingModes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
+        }
+    }
+
+    void removeStacksWithActivityTypes(int... activityTypes) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
+        }
+    }
+
+    ActivityRecord topRunningActivity() {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity();
+            if (topActivity != null) {
+                return topActivity;
+            }
+        }
+        return null;
+    }
+
+    boolean allResumedActivitiesIdle() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            // TODO(b/117135575): Check resumed activities on all visible stacks.
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            if (display.isSleeping()) {
+                // No resumed activities while display is sleeping.
+                continue;
+            }
+
+            // If the focused stack is not null or not empty, there should have some activities
+            // resuming or resumed. Make sure these activities are idle.
+            final ActivityStack stack = display.getFocusedStack();
+            if (stack == null || stack.numActivities() == 0) {
+                continue;
+            }
+            final ActivityRecord resumedActivity = stack.getResumedActivity();
+            if (resumedActivity == null || !resumedActivity.idle) {
+                if (DEBUG_STATES) {
+                    Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack="
+                            + stack.mStackId + " " + resumedActivity + " not idle");
+                }
+                return false;
+            }
+        }
+        // Send launch end powerhint when idle
+        sendPowerHintForLaunchEndIfNeeded();
+        return true;
+    }
+
+    boolean allResumedActivitiesVisible() {
+        boolean foundResumed = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.getResumedActivity();
+                if (r != null) {
+                    if (!r.nowVisible
+                            || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) {
+                        return false;
+                    }
+                    foundResumed = true;
+                }
+            }
+        }
+        return foundResumed;
+    }
+
+    boolean allPausedActivitiesComplete() {
+        boolean pausing = true;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.mPausingActivity;
+                if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
+                    if (DEBUG_STATES) {
+                        Slog.d(TAG_STATES,
+                                "allPausedActivitiesComplete: r=" + r + " state=" + r.getState());
+                        pausing = false;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        }
+        return pausing;
+    }
+
+    /**
+     * Find all visible task stacks containing {@param userId} and intercept them with an activity
+     * to block out the contents and possibly start a credential-confirming intent.
+     *
+     * @param userId user handle for the locked managed profile.
+     */
+    void lockAllProfileTasks(@UserIdInt int userId) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    final List<TaskRecord> tasks = stack.getAllTasks();
+                    for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
+                        final TaskRecord task = tasks.get(taskNdx);
+
+                        // Check the task for a top activity belonging to userId, or returning a
+                        // result to an activity belonging to userId. Example case: a document
+                        // picker for personal files, opened by a work app, should still get locked.
+                        if (taskTopActivityIsUser(task, userId)) {
+                            mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
+                                    task.taskId, userId);
+                        }
+                    }
+                }
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+        }
+    }
+
+    /**
+     * Detects whether we should show a lock screen in front of this task for a locked user.
+     * <p>
+     * We'll do this if either of the following holds:
+     * <ul>
+     *   <li>The top activity explicitly belongs to {@param userId}.</li>
+     *   <li>The top activity returns a result to an activity belonging to {@param userId}.</li>
+     * </ul>
+     *
+     * @return {@code true} if the top activity looks like it belongs to {@param userId}.
+     */
+    private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) {
+        // To handle the case that work app is in the task but just is not the top one.
+        final ActivityRecord activityRecord = task.getTopActivity();
+        final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
+
+        return (activityRecord != null && activityRecord.mUserId == userId)
+                || (resultTo != null && resultTo.mUserId == userId);
+    }
+
+    void cancelInitializingActivities() {
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                stack.cancelInitializingActivities();
+            }
+        }
+    }
+
+    TaskRecord anyTaskForId(int id) {
+        return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE);
+    }
+
+    TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) {
+        return anyTaskForId(id, matchMode, null, !ON_TOP);
+    }
+
+    /**
+     * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise.
+     * @param id Id of the task we would like returned.
+     * @param matchMode The mode to match the given task id in.
+     * @param aOptions The activity options to use for restoration. Can be null.
+     * @param onTop If the stack for the task should be the topmost on the display.
+     */
+    TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode,
+            @Nullable ActivityOptions aOptions, boolean onTop) {
+        // If options are set, ensure that we are attempting to actually restore a task
+        if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
+            throw new IllegalArgumentException("Should not specify activity options for non-restore"
+                    + " lookup");
+        }
+
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final TaskRecord task = stack.taskForIdLocked(id);
+                if (task == null) {
+                    continue;
+                }
+                if (aOptions != null) {
+                    // Resolve the stack the task should be placed in now based on options
+                    // and reparent if needed.
+                    final ActivityStack launchStack =
+                            getLaunchStack(null, aOptions, task, onTop);
+                    if (launchStack != null && stack != launchStack) {
+                        final int reparentMode = onTop
+                                ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE;
+                        task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME,
+                                "anyTaskForId");
+                    }
+                }
+                return task;
+            }
+        }
+
+        // If we are matching stack tasks only, return now
+        if (matchMode == MATCH_TASK_IN_STACKS_ONLY) {
+            return null;
+        }
+
+        // Otherwise, check the recent tasks and return if we find it there and we are not restoring
+        // the task from recents
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
+        final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id);
+
+        if (task == null) {
+            if (DEBUG_RECENTS) {
+                Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
+            }
+
+            return null;
+        }
+
+        if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) {
+            return task;
+        }
+
+        // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE
+        if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) {
+            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+                    "Couldn't restore task id=" + id + " found in recents");
+            return null;
+        }
+        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
+        return task;
+    }
+
+    ActivityRecord isInAnyStack(IBinder token) {
+        int numDisplays = mActivityDisplays.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityRecord r = stack.isInStackLocked(token);
+                if (r != null) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    @VisibleForTesting
+    void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
+            @WindowConfiguration.ActivityType int ignoreActivityType,
+            @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
+            boolean allowed) {
+        mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+                ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
+    }
+
+    void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
+        boolean sendHint = forceSend;
+
+        if (!sendHint) {
+            // Send power hint if we don't know what we're launching yet
+            sendHint = targetActivity == null || targetActivity.app == null;
+        }
+
+        if (!sendHint) { // targetActivity != null
+            // Send power hint when the activity's process is different than the current resumed
+            // activity on all displays, or if there are no resumed activities in the system.
+            boolean noResumedActivities = true;
+            boolean allFocusedProcessesDiffer = true;
+            for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+                final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+                final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
+                final WindowProcessController resumedActivityProcess =
+                        resumedActivity == null ? null : resumedActivity.app;
+
+                noResumedActivities &= resumedActivityProcess == null;
+                if (resumedActivityProcess != null) {
+                    allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app);
+                }
+            }
+            sendHint = noResumedActivities || allFocusedProcessesDiffer;
+        }
+
+        if (sendHint && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1);
+            mPowerHintSent = true;
+        }
+    }
+
+    void sendPowerHintForLaunchEndIfNeeded() {
+        // Trigger launch power hint if activity is launched
+        if (mPowerHintSent && mService.mPowerManagerInternal != null) {
+            mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0);
+            mPowerHintSent = false;
+        }
+    }
+
+    private void calculateDefaultMinimalSizeOfResizeableTasks() {
+        final Resources res = mService.mContext.getResources();
+        final float minimalSize = res.getDimension(
+                com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        final DisplayMetrics dm = res.getDisplayMetrics();
+
+        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
+    }
+
+    /**
+     * 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> getDumpActivities(String name, boolean dumpVisibleStacksOnly,
+            boolean dumpFocusedStackOnly) {
+        if (dumpFocusedStackOnly) {
+            return getTopDisplayFocusedStack().getDumpActivitiesLocked(name);
+        } else {
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            int numDisplays = mActivityDisplays.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getChildAt(stackNdx);
+                    if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
+                        activities.addAll(stack.getDumpActivitiesLocked(name));
+                    }
+                }
+            }
+            return activities;
+        }
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix);
+        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            display.dump(pw, prefix);
+        }
+    }
+
+    /**
+     * Dump all connected displays' configurations.
+     * @param prefix Prefix to apply to each line of the dump.
+     */
+    void dumpDisplayConfigs(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.println("Display override configurations:");
+        final int displayCount = mActivityDisplays.size();
+        for (int i = 0; i < displayCount; i++) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+            pw.print(prefix); pw.print("  "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
+            pw.println(activityDisplay.getOverrideConfiguration());
+        }
+    }
+
+    public void dumpDisplays(PrintWriter pw) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            pw.print("[id:" + display.mDisplayId + " stacks:");
+            display.dumpStacks(pw);
+            pw.print("]");
+        }
+    }
+
+    boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
+            String dumpPackage) {
+        boolean printed = false;
+        boolean needSep = false;
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+            pw.println(" (activities from top to bottom):");
+            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getChildAt(stackNdx);
+                pw.println();
+                pw.println("  Stack #" + stack.mStackId
+                        + ": type=" + activityTypeToString(stack.getActivityType())
+                        + " mode=" + windowingModeToString(stack.getWindowingMode()));
+                pw.println("  isSleeping=" + stack.shouldSleepActivities());
+                pw.println("  mBounds=" + stack.getOverrideBounds());
+
+                printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
+                        needSep);
+
+                printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, "    ", "Run", false,
+                        !dumpAll, false, dumpPackage, true,
+                        "    Running activities (most recent first):", null);
+
+                needSep = printed;
+                boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep,
+                        "    mPausingActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep,
+                        "    mResumedActivity: ");
+                if (pr) {
+                    printed = true;
+                    needSep = false;
+                }
+                if (dumpAll) {
+                    pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep,
+                            "    mLastPausedActivity: ");
+                    if (pr) {
+                        printed = true;
+                        needSep = true;
+                    }
+                    printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage,
+                            needSep, "    mLastNoHistoryActivity: ");
+                }
+                needSep = printed;
+            }
+            printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep,
+                    " ResumedActivity:");
+        }
+
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, "  ",
+                "Fin", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to finish:", null);
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, "  ",
+                "Stop", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to stop:", null);
+        printed |= dumpHistoryList(fd, pw,
+                mStackSupervisor.mActivitiesWaitingForVisibleActivity, "  ", "Wait",
+                false, !dumpAll, false, dumpPackage, true,
+                "  Activities waiting for another to become visible:", null);
+        printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities,
+                "  ", "Sleep", false, !dumpAll,
+                false, dumpPackage, true, "  Activities waiting to sleep:", null);
+
+        return printed;
+    }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
+        for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
+            final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
+            activityDisplay.writeToProto(proto, DISPLAYS);
+        }
+        mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
+        // TODO(b/111541062): Update tests to look for resumed activities on all displays
+        final ActivityStack focusedStack = getTopDisplayFocusedStack();
+        if (focusedStack != null) {
+            proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+            final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+            if (focusedActivity != null) {
+                focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+            }
+        } else {
+            proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+        }
+        proto.write(IS_HOME_RECENTS_COMPONENT,
+                mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
+        mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES);
+        proto.end(token);
+    }
+
+    private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken {
+        private final String mTag;
+        private final long mAcquireTime;
+        private final int mDisplayId;
+
+        public SleepTokenImpl(String tag, int displayId) {
+            mTag = tag;
+            mDisplayId = displayId;
+            mAcquireTime = SystemClock.uptimeMillis();
+        }
+
+        @Override
+        public void release() {
+            synchronized (mService.mGlobalLock) {
+                removeSleepToken(this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "{\"" + mTag + "\", display " + mDisplayId
+                    + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b483fd3..b98d154 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -82,12 +82,16 @@
 import java.util.function.Consumer;
 
 /** Root {@link WindowContainer} for the device. */
-class RootWindowContainer extends WindowContainer<DisplayContent> {
+class RootWindowContainer extends WindowContainer<DisplayContent>
+        implements ConfigurationContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM;
 
     private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1;
     private static final int SET_USER_ACTIVITY_TIMEOUT = 2;
 
+    // TODO: Remove after object merge with RootActivityContainer.
+    private RootActivityContainer mRootActivityContainer;
+
     private Object mLastWindowFreezeSource = null;
     private Session mHoldScreen = null;
     private float mScreenBrightness = -1;
@@ -145,6 +149,13 @@
         mHandler = new MyHandler(service.mH.getLooper());
     }
 
+    void setRootActivityContainer(RootActivityContainer container) {
+        mRootActivityContainer = container;
+        if (container != null) {
+            container.registerConfigurationChangeListener(this);
+        }
+    }
+
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
         boolean changed = false;
         int topFocusedDisplayId = INVALID_DISPLAY;
@@ -171,7 +182,7 @@
                 mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL;
         if (mTopFocusedDisplayId != topFocusedDisplayId) {
             mTopFocusedDisplayId = topFocusedDisplayId;
-            mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
+            mWmService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId);
             if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
                     + mTopFocusedDisplayId);
         }
@@ -183,15 +194,13 @@
             final boolean isTopFocusedDisplay =
                     topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId;
             if (windowFocusChanged || isTopFocusedDisplay) {
-                final Message msg = mService.mH.obtainMessage(
+                final Message msg = mWmService.mH.obtainMessage(
                         WindowManagerService.H.REPORT_FOCUS_CHANGE, dc);
                 msg.arg1 = topFocusedDisplayChanged ? 1 : 0;
-                mService.mH.sendMessage(msg);
+                mWmService.mH.sendMessage(msg);
             }
         });
-        final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
-        mService.mInputManager.setFocusedWindow(
-                topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+
         return changed;
     }
 
@@ -202,7 +211,7 @@
 
     @Override
     void onChildPositionChanged() {
-        mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
+        mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
     }
 
     DisplayContent getDisplayContent(int displayId) {
@@ -224,27 +233,42 @@
         final DisplayContent existing = getDisplayContent(displayId);
 
         if (existing != null) {
+            initializeDisplayOverrideConfiguration(controller, existing);
             existing.setController(controller);
             return existing;
         }
 
-        final DisplayContent dc = new DisplayContent(display, mService, controller);
+        final DisplayContent dc = new DisplayContent(display, mWmService, controller);
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
-        mService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+        mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
+        initializeDisplayOverrideConfiguration(controller, dc);
 
-        if (mService.mDisplayManagerInternal != null) {
-            mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
+        if (mWmService.mDisplayManagerInternal != null) {
+            mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
                     displayId, dc.getDisplayInfo());
             dc.configureDisplayPolicy();
         }
 
-        mService.reconfigureDisplayLocked(dc);
+        mWmService.reconfigureDisplayLocked(dc);
 
         return dc;
     }
 
+    /**
+     * The display content may have configuration set from {@link #DisplayWindowSettings}. This
+     * callback let the owner of container know there is existing configuration to prevent the
+     * values from being replaced by the initializing {@link #ActivityDisplay}.
+     */
+    private void initializeDisplayOverrideConfiguration(DisplayWindowController controller,
+            DisplayContent displayContent) {
+        if (controller != null && controller.mListener != null) {
+            controller.mListener.onInitializeOverrideConfiguration(
+                    displayContent.getOverrideConfiguration());
+        }
+    }
+
     boolean isLayoutNeeded() {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
@@ -412,11 +436,11 @@
 
     void removeReplacedWindows() {
         if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION removeReplacedWindows");
-        mService.openSurfaceTransaction();
+        mWmService.openSurfaceTransaction();
         try {
             forAllWindows(sRemoveReplacedWindowsConsumer, true /* traverseTopToBottom */);
         } finally {
-            mService.closeSurfaceTransaction("removeReplacedWindows");
+            mWmService.closeSurfaceTransaction("removeReplacedWindows");
             if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION removeReplacedWindows");
         }
     }
@@ -464,7 +488,7 @@
                 final SparseIntArray pidCandidates = new SparseIntArray();
                 for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                     mChildren.get(displayNdx).forAllWindows((w) -> {
-                        if (mService.mForceRemoves.contains(w)) {
+                        if (mWmService.mForceRemoves.contains(w)) {
                             return;
                         }
                         final WindowStateAnimator wsa = w.mWinAnimator;
@@ -479,7 +503,7 @@
                             pids[i] = pidCandidates.keyAt(i);
                         }
                         try {
-                            if (mService.mActivityManager.killPids(pids, "Free memory", secure)) {
+                            if (mWmService.mActivityManager.killPids(pids, "Free memory", secure)) {
                                 killedApps = true;
                             }
                         } catch (RemoteException e) {
@@ -497,9 +521,8 @@
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin,
                             "RECOVER DESTROY", false);
                     winAnimator.destroySurface();
-                    if (winAnimator.mWin.mAppToken != null
-                            && winAnimator.mWin.mAppToken.getController() != null) {
-                        winAnimator.mWin.mAppToken.getController().removeStartingWindow();
+                    if (winAnimator.mWin.mAppToken != null) {
+                        winAnimator.mWin.mAppToken.removeStartingWindow();
                     }
                 }
 
@@ -524,9 +547,9 @@
         int i;
         boolean updateInputWindowsNeeded = false;
 
-        if (mService.mFocusMayChange) {
-            mService.mFocusMayChange = false;
-            updateInputWindowsNeeded = mService.updateFocusedWindowLocked(
+        if (mWmService.mFocusMayChange) {
+            mWmService.mFocusMayChange = false;
+            updateInputWindowsNeeded = mWmService.updateFocusedWindowLocked(
                     UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
         }
 
@@ -542,31 +565,31 @@
         mUserActivityTimeout = -1;
         mObscureApplicationContentOnSecondaryDisplays = false;
         mSustainedPerformanceModeCurrent = false;
-        mService.mTransactionSequence++;
+        mWmService.mTransactionSequence++;
 
         // TODO(multi-display): recents animation & wallpaper need support multi-display.
-        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
-        final WindowSurfacePlacer surfacePlacer = mService.mWindowPlacerLocked;
+        final DisplayContent defaultDisplay = mWmService.getDefaultDisplayContentLocked();
+        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
 
         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-        mService.openSurfaceTransaction();
+        mWmService.openSurfaceTransaction();
         try {
             applySurfaceChangesTransaction(recoveringMemory);
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
-            mService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
+            mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
             if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                     "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
         }
-        mService.mAnimator.executeAfterPrepareSurfacesRunnables();
+        mWmService.mAnimator.executeAfterPrepareSurfacesRunnables();
 
         checkAppTransitionReady(surfacePlacer);
 
         // Defer starting the recents animation until the wallpaper has drawn
         final RecentsAnimationController recentsAnimationController =
-                mService.getRecentsAnimationController();
+                mWmService.getRecentsAnimationController();
         if (recentsAnimationController != null) {
             recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
         }
@@ -583,9 +606,9 @@
             }
         }
 
-        if (mService.mFocusMayChange) {
-            mService.mFocusMayChange = false;
-            if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
+        if (mWmService.mFocusMayChange) {
+            mWmService.mFocusMayChange = false;
+            if (mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                     false /*updateInputWindows*/)) {
                 updateInputWindowsNeeded = true;
             }
@@ -599,23 +622,23 @@
 
         handleResizingWindows();
 
-        if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
+        if (DEBUG_ORIENTATION && mWmService.mDisplayFrozen) Slog.v(TAG,
                 "With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
         if (mOrientationChangeComplete) {
-            if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
-                mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
-                mService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
-                mService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
+            if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+                mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+                mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
+                mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
             }
-            mService.stopFreezingDisplayLocked();
+            mWmService.stopFreezingDisplayLocked();
         }
 
         // Destroy the surface of any windows that are no longer visible.
-        i = mService.mDestroySurface.size();
+        i = mWmService.mDestroySurface.size();
         if (i > 0) {
             do {
                 i--;
-                WindowState win = mService.mDestroySurface.get(i);
+                WindowState win = mWmService.mDestroySurface.get(i);
                 win.mDestroying = false;
                 final DisplayContent displayContent = win.getDisplayContent();
                 if (displayContent.mInputMethodWindow == win) {
@@ -627,7 +650,7 @@
                 win.destroySurfaceUnchecked();
                 win.mWinAnimator.destroyPreservedSurfaceLocked();
             } while (i > 0);
-            mService.mDestroySurface.clear();
+            mWmService.mDestroySurface.clear();
         }
 
         // Time to remove any exiting tokens?
@@ -648,8 +671,8 @@
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
         });
 
-        mService.setHoldScreenLocked(mHoldScreen);
-        if (!mService.mDisplayFrozen) {
+        mWmService.setHoldScreenLocked(mHoldScreen);
+        if (!mWmService.mDisplayFrozen) {
             final int brightness = mScreenBrightness < 0 || mScreenBrightness > 1.0f
                     ? -1 : toBrightnessOverride(mScreenBrightness);
 
@@ -661,7 +684,7 @@
 
         if (mSustainedPerformanceModeCurrent != mSustainedPerformanceModeEnabled) {
             mSustainedPerformanceModeEnabled = mSustainedPerformanceModeCurrent;
-            mService.mPowerManagerInternal.powerHint(
+            mWmService.mPowerManagerInternal.powerHint(
                     PowerHint.SUSTAINED_PERFORMANCE,
                     (mSustainedPerformanceModeEnabled ? 1 : 0));
         }
@@ -671,22 +694,22 @@
             mUpdateRotation = updateRotationUnchecked();
         }
 
-        if (mService.mWaitingForDrawnCallback != null
+        if (mWmService.mWaitingForDrawnCallback != null
                 || (mOrientationChangeComplete && !isLayoutNeeded()
                         && !mUpdateRotation)) {
-            mService.checkDrawnWindowsLocked();
+            mWmService.checkDrawnWindowsLocked();
         }
 
-        final int N = mService.mPendingRemove.size();
+        final int N = mWmService.mPendingRemove.size();
         if (N > 0) {
-            if (mService.mPendingRemoveTmp.length < N) {
-                mService.mPendingRemoveTmp = new WindowState[N + 10];
+            if (mWmService.mPendingRemoveTmp.length < N) {
+                mWmService.mPendingRemoveTmp = new WindowState[N + 10];
             }
-            mService.mPendingRemove.toArray(mService.mPendingRemoveTmp);
-            mService.mPendingRemove.clear();
+            mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
+            mWmService.mPendingRemove.clear();
             ArrayList<DisplayContent> displayList = new ArrayList();
             for (i = 0; i < N; i++) {
-                final WindowState w = mService.mPendingRemoveTmp[i];
+                final WindowState w = mWmService.mPendingRemoveTmp[i];
                 w.removeImmediately();
                 final DisplayContent displayContent = w.getDisplayContent();
                 if (displayContent != null && !displayList.contains(displayContent)) {
@@ -714,12 +737,13 @@
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
-        mService.enableScreenIfNeededLocked();
+        mWmService.enableScreenIfNeededLocked();
 
-        mService.scheduleAnimationLocked();
+        mWmService.scheduleAnimationLocked();
 
         if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
-                "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
+                "performSurfacePlacementInner exit: animating="
+                        + mWmService.mAnimator.isAnimating());
     }
 
     private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
@@ -759,23 +783,23 @@
         mObscuringWindow = null;
 
         // TODO(multi-display): Support these features on secondary screens.
-        final DisplayContent defaultDc = mService.getDefaultDisplayContentLocked();
+        final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
         final DisplayInfo defaultInfo = defaultDc.getDisplayInfo();
         final int defaultDw = defaultInfo.logicalWidth;
         final int defaultDh = defaultInfo.logicalHeight;
-        if (mService.mWatermark != null) {
-            mService.mWatermark.positionSurface(defaultDw, defaultDh);
+        if (mWmService.mWatermark != null) {
+            mWmService.mWatermark.positionSurface(defaultDw, defaultDh);
         }
-        if (mService.mStrictModeFlash != null) {
-            mService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+        if (mWmService.mStrictModeFlash != null) {
+            mWmService.mStrictModeFlash.positionSurface(defaultDw, defaultDh);
         }
-        if (mService.mCircularDisplayMask != null) {
-            mService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
-                    mService.getDefaultDisplayRotation());
+        if (mWmService.mCircularDisplayMask != null) {
+            mWmService.mCircularDisplayMask.positionSurface(defaultDw, defaultDh,
+                    mWmService.getDefaultDisplayRotation());
         }
-        if (mService.mEmulatorDisplayOverlay != null) {
-            mService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
-                    mService.getDefaultDisplayRotation());
+        if (mWmService.mEmulatorDisplayOverlay != null) {
+            mWmService.mEmulatorDisplayOverlay.positionSurface(defaultDw, defaultDh,
+                    mWmService.getDefaultDisplayRotation());
         }
 
         final int count = mChildren.size();
@@ -786,7 +810,7 @@
 
         // Give the display manager a chance to adjust properties like display rotation if it needs
         // to.
-        mService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
+        mWmService.mDisplayManagerInternal.performTraversal(mDisplayTransaction);
         SurfaceControl.mergeToGlobalTransaction(mDisplayTransaction);
     }
 
@@ -794,14 +818,14 @@
      * Handles resizing windows during surface placement.
      */
     private void handleResizingWindows() {
-        for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
-            WindowState win = mService.mResizingWindows.get(i);
+        for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
+            WindowState win = mWmService.mResizingWindows.get(i);
             if (win.mAppFreezing) {
                 // Don't remove this window until rotation has completed.
                 continue;
             }
             win.reportResized();
-            mService.mResizingWindows.remove(i);
+            mWmService.mResizingWindows.remove(i);
         }
     }
 
@@ -839,7 +863,7 @@
             if ((attrFlags & FLAG_KEEP_SCREEN_ON) != 0) {
                 mHoldScreen = w.mSession;
                 mHoldScreenWindow = w;
-            } else if (DEBUG_KEEP_SCREEN_ON && w == mService.mLastWakeLockHoldingWindow) {
+            } else if (DEBUG_KEEP_SCREEN_ON && w == mWmService.mLastWakeLockHoldingWindow) {
                 Slog.d(TAG_KEEP_SCREEN_ON, "handleNotObscuredLocked: " + w + " was holding "
                         + "screen wakelock but no longer has FLAG_KEEP_SCREEN_ON!!! called by"
                         + Debug.getCallers(10));
@@ -888,7 +912,7 @@
     boolean copyAnimToLayoutParams() {
         boolean doRequest = false;
 
-        final int bulkUpdateParams = mService.mAnimator.mBulkUpdateParams;
+        final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
         if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
             mUpdateRotation = true;
             doRequest = true;
@@ -897,8 +921,8 @@
             mOrientationChangeComplete = false;
         } else {
             mOrientationChangeComplete = true;
-            mLastWindowFreezeSource = mService.mAnimator.mLastWindowFreezeSource;
-            if (mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
+            mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
+            if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                 doRequest = true;
             }
         }
@@ -924,12 +948,12 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case SET_SCREEN_BRIGHTNESS_OVERRIDE:
-                    mService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
+                    mWmService.mPowerManagerInternal.setScreenBrightnessOverrideFromWindowManager(
                             msg.arg1);
                     break;
                 case SET_USER_ACTIVITY_TIMEOUT:
-                    mService.mPowerManagerInternal.setUserActivityTimeoutOverrideFromWindowManager(
-                            (Long) msg.obj);
+                    mWmService.mPowerManagerInternal.
+                            setUserActivityTimeoutOverrideFromWindowManager((Long) msg.obj);
                     break;
                 default:
                     break;
@@ -939,7 +963,7 @@
 
     void dumpDisplayContents(PrintWriter pw) {
         pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
-        if (mService.mDisplayReady) {
+        if (mWmService.mDisplayReady) {
             final int count = mChildren.size();
             for (int i = 0; i < count; ++i) {
                 final DisplayContent displayContent = mChildren.get(i);
@@ -992,7 +1016,7 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {
         final long token = proto.start(fieldId);
         super.writeToProto(proto, WINDOW_CONTAINER, trim);
-        if (mService.mDisplayReady) {
+        if (mWmService.mDisplayReady) {
             final int count = mChildren.size();
             for (int i = 0; i < count; ++i) {
                 final DisplayContent displayContent = mChildren.get(i);
@@ -1015,9 +1039,8 @@
     @Override
     void positionChildAt(int position, DisplayContent child, boolean includingParents) {
         super.positionChildAt(position, child, includingParents);
-        final RootWindowContainerController controller = getController();
-        if (controller != null) {
-            controller.onChildPositionChanged(child, position);
+        if (mRootActivityContainer != null) {
+            mRootActivityContainer.onChildPositionChanged(child.getController(), position);
         }
     }
 
@@ -1027,13 +1050,8 @@
     }
 
     @Override
-    RootWindowContainerController getController() {
-        return (RootWindowContainerController) super.getController();
-    }
-
-    @Override
     void scheduleAnimation() {
-        mService.scheduleAnimationLocked();
+        mWmService.scheduleAnimationLocked();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
deleted file mode 100644
index 1176220..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerController.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.wm;
-
-/**
- * Controller for the root container. This is created by activity manager to link activity
- * stack supervisor to the root window container they use in window manager.
- */
-public class RootWindowContainerController
-        extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> {
-
-    public RootWindowContainerController(RootWindowContainerListener listener) {
-        super(listener, WindowManagerService.getInstance());
-        synchronized (mGlobalLock) {
-            mRoot.setController(this);
-        }
-    }
-
-    void onChildPositionChanged(DisplayContent child, int position) {
-        // This callback invokes to AM directly so here assumes AM lock is held. If there is another
-        // path called only with WM lock, it should change to use handler to post or move outside of
-        // WM lock with adding AM lock.
-        mListener.onChildPositionChanged(child.getController(), position);
-    }
-
-    /** Move the display to the given position. */
-    public void positionChildAt(DisplayWindowController child, int position) {
-        synchronized (mGlobalLock) {
-            mContainer.positionChildAt(position, child.mContainer);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/wm/RootWindowContainerListener.java
deleted file mode 100644
index f413e3f7..0000000
--- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2018 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.wm;
-
-/**
- * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to
- * the display container in activity manager.
- */
-public interface RootWindowContainerListener extends WindowContainerListener {
-    /** Called when the z-order of display is changed. */
-    void onChildPositionChanged(DisplayWindowController childController, int position);
-}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index df97027..3947bd4 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -222,7 +222,7 @@
     }
 
     public ScreenRotationAnimation(Context context, DisplayContent displayContent,
-            boolean forceDefaultOrientation, boolean isSecure, WindowManagerService service) {
+            boolean fixedToUserRotation, boolean isSecure, WindowManagerService service) {
         mService = service;
         mContext = context;
         mDisplayContent = displayContent;
@@ -234,7 +234,7 @@
         final int originalWidth;
         final int originalHeight;
         DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        if (forceDefaultOrientation) {
+        if (fixedToUserRotation) {
             // Emulated orientation.
             mForceDefaultOrientation = true;
             originalWidth = displayContent.mBaseDisplayWidth;
@@ -261,7 +261,7 @@
         try {
             mSurfaceControl = displayContent.makeOverlay()
                     .setName("ScreenshotSurface")
-                    .setSize(mWidth, mHeight)
+                    .setBufferSize(mWidth, mHeight)
                     .setSecure(isSecure)
                     .build();
 
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 95ca0a6..05f556c 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -89,13 +89,16 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
+     *
+     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
+     * it.
      */
-    public void finish(WindowState win) {
+    public void finish(WindowState win, boolean timeout) {
         mTransform.reset();
         final Transaction t = win.getPendingTransaction();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
         t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
-        if (win.mWinAnimator.mSurfaceController != null) {
+        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
             t.deferTransactionUntil(win.mSurfaceControl,
                     win.mWinAnimator.mSurfaceController.getHandle(), win.getFrameNumber());
             t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 6838c55..37b5a7c 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -50,6 +50,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.InsetsState;
 import android.view.WindowManager;
 
 import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -153,17 +154,21 @@
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
             Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
-                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,
+                outInsetsState);
     }
 
     @Override
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
+            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
+            InsetsState outInsetsState) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
-                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */);
+                new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
+                outInsetsState);
     }
 
     @Override
@@ -182,7 +187,7 @@
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
             Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -190,7 +195,7 @@
                 requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
-                mergedConfiguration, outSurface);
+                mergedConfiguration, outSurface, outInsetsState);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index 35264a2..8f18aa5 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -252,7 +252,7 @@
      * before mContainer has been updated, any relevant properties (like {@param windowingMode})
      * need to be passed in.
      */
-    public void adjustConfigurationForBounds(Rect bounds, Rect insetBounds,
+    public void adjustConfigurationForBounds(Rect bounds,
             Rect nonDecorBounds, Rect stableBounds, boolean overrideWidth,
             boolean overrideHeight, float density, Configuration config,
             Configuration parentConfig, int windowingMode) {
@@ -303,11 +303,9 @@
                 // Additionally task dimensions should not be bigger than its parents dimensions.
                 // The non decor inset are areas that could never be removed in Honeycomb. See
                 // {@link WindowManagerPolicy#getNonDecorInsetsLw}.
-                intersectDisplayBoundsExcludeInsets(nonDecorBounds,
-                        insetBounds != null ? insetBounds : bounds, mTmpNonDecorInsets,
+                intersectDisplayBoundsExcludeInsets(nonDecorBounds, bounds, mTmpNonDecorInsets,
                         mTmpDisplayBounds, overrideWidth, overrideHeight);
-                intersectDisplayBoundsExcludeInsets(stableBounds,
-                        insetBounds != null ? insetBounds : bounds, mTmpStableInsets,
+                intersectDisplayBoundsExcludeInsets(stableBounds, bounds, mTmpStableInsets,
                         mTmpDisplayBounds, overrideWidth, overrideHeight);
                 width = Math.min((int) (stableBounds.width() / density),
                         parentConfig.screenWidthDp);
@@ -323,7 +321,7 @@
             config.screenWidthDp = width;
             config.screenHeightDp = height;
             config.smallestScreenWidthDp = getSmallestWidthForTaskBounds(
-                    insetBounds != null ? insetBounds : bounds, density, windowingMode);
+                    bounds, density, windowingMode);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index e97b366..82f2ad8 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -24,12 +23,9 @@
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 class StrictModeFlash {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -46,7 +42,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("StrictModeFlash")
-                    .setSize(1, 1)
+                    .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);  // one more than Watermark? arbitrary.
@@ -122,7 +118,7 @@
         }
         mLastDW = dw;
         mLastDH = dh;
-        mSurfaceControl.setSize(dw, dh);
+        mSurfaceControl.setBufferSize(dw, dh);
         mDrawNeeded = true;
     }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 31c0c7f..11068ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -302,8 +302,7 @@
         if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash");
         final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash()
                 .setParent(mAnimatable.getAnimationLeashParent())
-                .setName(surface + " - animation-leash")
-                .setSize(width, height);
+                .setName(surface + " - animation-leash");
         final SurfaceControl leash = builder.build();
         t.setWindowCrop(leash, width, height);
         if (!hidden) {
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index 0c9a14b..bbe5424 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -1,17 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "CtsWindowManagerDeviceTestCases",
-      "options": [
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "android.support.test.filters.FlakyTest"
-        }
-      ]
-    },
-    {
       "name": "FrameworksServicesTests",
       "options": [
         {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c9800f8..c70f075 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -27,11 +27,11 @@
 import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
 import static com.android.server.wm.TaskProto.ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
 import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -66,8 +66,8 @@
     final Rect mPreparedFrozenBounds = new Rect();
     final Configuration mPreparedFrozenMergedConfig = new Configuration();
 
-    // Bounds used to calculate the insets.
-    private final Rect mTempInsetBounds = new Rect();
+    // If non-empty, bounds used to display the task during animations/interactions.
+    private final Rect mOverrideDisplayedBounds = new Rect();
 
     /** ID of the display which rotation {@link #mRotation} has. */
     private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
@@ -302,29 +302,28 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        updateSurfaceSize(dc);
         adjustBoundsForDisplayChangeIfNeeded(dc);
         super.onDisplayChanged(dc);
     }
 
     /**
-     * Sets the bounds used to calculate the insets. See
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
+     * Sets bounds that override where the task is displayed. Used during transient operations
+     * like animation / interaction.
      */
-    void setTempInsetBounds(Rect tempInsetBounds) {
-        if (tempInsetBounds != null) {
-            mTempInsetBounds.set(tempInsetBounds);
+    void setOverrideDisplayedBounds(Rect overrideDisplayedBounds) {
+        if (overrideDisplayedBounds != null) {
+            mOverrideDisplayedBounds.set(overrideDisplayedBounds);
         } else {
-            mTempInsetBounds.setEmpty();
+            mOverrideDisplayedBounds.setEmpty();
         }
     }
 
     /**
-     * Gets the bounds used to calculate the insets. See
+     * Gets the bounds that override where the task is displayed. See
      * {@link android.app.IActivityTaskManager#resizeDockedStack} why this is needed.
      */
-    void getTempInsetBounds(Rect out) {
-        out.set(mTempInsetBounds);
+    Rect getOverrideDisplayedBounds() {
+        return mOverrideDisplayedBounds;
     }
 
     void setResizeable(int resizeMode) {
@@ -333,7 +332,7 @@
 
     boolean isResizeable() {
         return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
-                || mService.mForceResizableTasks;
+                || mWmService.mForceResizableTasks;
     }
 
     /**
@@ -381,8 +380,13 @@
         } else {
             mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
         }
-        setTempInsetBounds(tempInsetBounds);
-        setBounds(mTmpRect2, false /* forced */);
+        if (tempInsetBounds == null || tempInsetBounds.isEmpty()) {
+            setOverrideDisplayedBounds(null);
+            setBounds(mTmpRect2);
+        } else {
+            setOverrideDisplayedBounds(mTmpRect2);
+            setBounds(tempInsetBounds);
+        }
     }
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
@@ -408,6 +412,15 @@
         mStack.getDisplayContent().getBounds(out);
     }
 
+    @Override
+    public Rect getDisplayedBounds() {
+        if (mOverrideDisplayedBounds.isEmpty()) {
+            return super.getDisplayedBounds();
+        } else {
+            return mOverrideDisplayedBounds;
+        }
+    }
+
     /**
      * Calculate the maximum visible area of this task. If the task has only one app,
      * the result will be visible frame of that app. If the task has more than one apps,
@@ -582,7 +595,7 @@
     }
 
     boolean isTaskAnimating() {
-        final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController();
+        final RecentsAnimationController recentsAnim = mWmService.getRecentsAnimationController();
         if (recentsAnim != null) {
             if (recentsAnim.isAnimatingTask(this)) {
                 return true;
@@ -629,13 +642,13 @@
     }
 
     void forceWindowsScaleable(boolean force) {
-        mService.openSurfaceTransaction();
+        mWmService.openSurfaceTransaction();
         try {
             for (int i = mChildren.size() - 1; i >= 0; i--) {
                 mChildren.get(i).forceWindowsScaleableInTransaction(force);
             }
         } finally {
-            mService.closeSurfaceTransaction("forceWindowsScaleable");
+            mWmService.closeSurfaceTransaction("forceWindowsScaleable");
         }
     }
 
@@ -724,7 +737,7 @@
         }
         proto.write(FILLS_PARENT, matchParentBounds());
         getBounds().writeToProto(proto, BOUNDS);
-        mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS);
+        mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
         proto.write(DEFER_REMOVAL, mDeferRemoval);
         proto.write(SURFACE_WIDTH, mSurfaceControl.getWidth());
         proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
@@ -740,7 +753,7 @@
         pw.println(doublePrefix + "mBounds=" + getBounds().toShortString());
         pw.println(doublePrefix + "mdr=" + mDeferRemoval);
         pw.println(doublePrefix + "appTokens=" + mChildren);
-        pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString());
+        pw.println(doublePrefix + "mDisplayedBounds=" + mOverrideDisplayedBounds.toShortString());
 
         final String triplePrefix = doublePrefix + "  ";
         final String quadruplePrefix = triplePrefix + "  ";
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 3b3feac..bb3df02 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -283,8 +283,8 @@
     void notifyActivityPinned(ActivityRecord r) {
         mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
         final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
-                r.getTask().taskId, r.getStackId(), r.packageName);
-        msg.sendingUid = r.userId;
+                r.getTaskRecord().taskId, r.getStackId(), r.packageName);
+        msg.sendingUid = r.mUserId;
         forAllLocalListeners(mNotifyActivityPinned, msg);
         msg.sendToTarget();
     }
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 117984a..4ae2a79 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -138,7 +138,7 @@
         // STEP 1: Determine the display to launch the activity/task.
         final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams);
         outParams.mPreferredDisplayId = displayId;
-        ActivityDisplay display = mSupervisor.getActivityDisplay(displayId);
+        ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId);
         if (DEBUG) {
             appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode="
                     + display.getWindowingMode());
@@ -300,12 +300,14 @@
             displayId = stack.mDisplayId;
         }
 
-        if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) {
+        if (displayId != INVALID_DISPLAY
+                && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) {
             displayId = currentParams.mPreferredDisplayId;
         }
         displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId;
 
-        return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null)
+        return (displayId != INVALID_DISPLAY
+                && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null)
                 ? displayId : DEFAULT_DISPLAY;
     }
 
@@ -606,7 +608,8 @@
                 || displayBounds.height() < inOutBounds.height()) {
             // There is no way for us to fit the bounds in the display without changing width
             // or height. Just move the start to align with the display.
-            final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection();
+            final int layoutDirection =
+                    mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection();
             final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
                     ? displayBounds.width() - inOutBounds.width()
                     : 0;
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 8120dec..d50af38 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
 
 import android.annotation.NonNull;
 import android.graphics.Bitmap;
@@ -330,7 +330,7 @@
                                 // mWriteQueue.add(new TaskWriteQueueItem(task));
 
                                 final int taskId = task.taskId;
-                                if (mStackSupervisor.anyTaskForIdLocked(taskId,
+                                if (mService.mRootActivityContainer.anyTaskForId(taskId,
                                         MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) {
                                     // Should not happen.
                                     Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 7182ad6..b88e581 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -33,6 +33,7 @@
 import android.app.IActivityTaskManager;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
@@ -261,7 +262,7 @@
                 mClientChannel, mService.mAnimationHandler.getLooper(),
                 mService.mAnimator.getChoreographer());
 
-        mDragApplicationHandle = new InputApplicationHandle(null);
+        mDragApplicationHandle = new InputApplicationHandle(new Binder());
         mDragApplicationHandle.name = TAG;
         mDragApplicationHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -269,7 +270,7 @@
         mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
                 display.getDisplayId());
         mDragWindowHandle.name = TAG;
-        mDragWindowHandle.inputChannel = mServerChannel;
+        mDragWindowHandle.token = mServerChannel.getToken();
         mDragWindowHandle.layer = mService.getDragLayerLocked();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 51567a0..e15bf5b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -21,15 +21,19 @@
 
 import android.annotation.Nullable;
 import android.app.IActivityTaskManager;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.Display;
 import android.view.IWindow;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.input.InputManagerService;
-import android.view.InputWindowHandle;
 
 /**
  * Controller for task positioning by drag.
@@ -39,10 +43,14 @@
     private final InputManagerService mInputManager;
     private final IActivityTaskManager mActivityManager;
     private final Handler mHandler;
+    private SurfaceControl mInputSurface;
+    private DisplayContent mPositioningDisplay;
 
     @GuardedBy("WindowManagerSerivce.mWindowMap")
     private @Nullable TaskPositioner mTaskPositioner;
 
+    private final Rect mTmpClipRect = new Rect();
+
     boolean isPositioningLocked() {
         return mTaskPositioner != null;
     }
@@ -59,6 +67,43 @@
         mHandler = new Handler(looper);
     }
 
+    void hideInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId
+                && mInputSurface != null) {
+            t.hide(mInputSurface);
+        }
+    }
+
+    void showInputSurface(SurfaceControl.Transaction t, int displayId) {
+        if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) {
+            return;
+        }
+        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+        if (mInputSurface == null) {
+            mInputSurface = mService.makeSurfaceBuilder(dc.getSession())
+                    .setContainerLayer(true)
+                    .setName("Drag and Drop Input Consumer").build();
+        }
+
+        final InputWindowHandle h = getDragWindowHandleLocked();
+        if (h == null) {
+            Slog.w(TAG_WM, "Drag is in progress but there is no "
+                    + "drag window handle.");
+            return;
+        }
+
+        t.show(mInputSurface);
+        t.setInputWindowInfo(mInputSurface, h);
+        t.setLayer(mInputSurface, Integer.MAX_VALUE);
+
+        final Display display = dc.getDisplay();
+        final Point p = new Point();
+        display.getRealSize(p);
+
+        mTmpClipRect.set(0, 0, p.x, p.y);
+        t.setWindowCrop(mInputSurface, mTmpClipRect);
+    }
+
     boolean startMovingTask(IWindow window, float startX, float startY) {
         WindowState win = null;
         synchronized (mService.mGlobalLock) {
@@ -122,6 +167,7 @@
             Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
             return false;
         }
+        mPositioningDisplay = displayContent;
 
         mTaskPositioner = TaskPositioner.create(mService);
         mTaskPositioner.register(displayContent);
@@ -138,9 +184,7 @@
         if (!mInputManager.transferTouchFocus(
                 transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
             Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
-            mTaskPositioner.unregister();
-            mTaskPositioner = null;
-            displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
+            cleanUpTaskPositioner();
             return false;
         }
 
@@ -153,11 +197,21 @@
             if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
 
             synchronized (mService.mGlobalLock) {
-                if (mTaskPositioner != null) {
-                    mTaskPositioner.unregister();
-                    mTaskPositioner = null;
-                }
+                cleanUpTaskPositioner();
+                mPositioningDisplay = null;
             }
         });
     }
+
+    private void cleanUpTaskPositioner() {
+        final TaskPositioner positioner = mTaskPositioner;
+        if (positioner == null) {
+            return;
+        }
+
+        // We need to assign task positioner to null first to indicate that we're finishing task
+        // positioning.
+        mTaskPositioner = null;
+        positioner.unregister();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index eec10ab..3cf0bd7 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -310,6 +310,11 @@
     // This number will be assigned when we evaluate OOM scores for all visible tasks.
     int mLayerRank = -1;
 
+    // When non-empty, this represents the bounds this task will be drawn at. This gets set during
+    // transient operations such as split-divider dragging and animations.
+    // TODO(b/119687367): This member is temporary.
+    final Rect mDisplayedBounds = new Rect();
+
     /** Helper object used for updating override configuration. */
     private Configuration mTmpConfig = new Configuration();
 
@@ -447,6 +452,9 @@
         }
 
         mWindowContainerController = controller;
+        if (!mDisplayedBounds.isEmpty() && controller.mContainer != null) {
+            controller.mContainer.setOverrideDisplayedBounds(mDisplayedBounds);
+        }
     }
 
     void removeWindowContainer() {
@@ -472,8 +480,8 @@
         }
         mResizeMode = resizeMode;
         mWindowContainerController.setResizeable(resizeMode);
-        mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+        mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
     }
 
     void setTaskDockedResizing(boolean resizing) {
@@ -544,10 +552,9 @@
                     // this won't cause tons of irrelevant windows being preserved because only
                     // activities in this task may experience a bounds change. Configs for other
                     // activities stay the same.
-                    mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
-                            preserveWindow);
+                    mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow);
                     if (!kept) {
-                        mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
+                        mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
                     }
                 }
             }
@@ -623,6 +630,7 @@
             @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
             boolean schedulePictureInPictureModeChange, String reason) {
         final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
+        final RootActivityContainer root = mService.mRootActivityContainer;
         final WindowManagerService windowManager = mService.mWindowManager;
         final ActivityStack sourceStack = getStack();
         final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack,
@@ -655,7 +663,7 @@
         boolean kept = true;
         try {
             final ActivityRecord r = topRunningActivityLocked();
-            final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack)
+            final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack)
                     && (topRunningActivityLocked() == r);
             final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r;
             final boolean wasPaused = r != null && sourceStack.mPausingActivity == r;
@@ -748,8 +756,8 @@
         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.resumeFocusedStacksTopActivitiesLocked();
+            root.ensureActivitiesVisible(null, 0, !mightReplaceWindow);
+            root.resumeFocusedStacksTopActivities();
         }
 
         // TODO: Handle incorrect request to move before the actual move, not after.
@@ -903,7 +911,7 @@
         // Correct the activity intent for aliasing. The task record intent will always be based on
         // the real activity that will be launched not the alias, so we need to use an intent with
         // the component name pointing to the real activity not the alias in the activity record.
-        intent.setComponent(r.realActivity);
+        intent.setComponent(r.mActivityComponent);
         return intent.filterEquals(this.intent);
     }
 
@@ -982,7 +990,7 @@
     @Override
     protected void onParentChanged() {
         super.onParentChanged();
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+        mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     // Close up recents linked list.
@@ -1143,7 +1151,7 @@
     }
 
     boolean okToShowLocked() {
-        // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
+        // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is
         // okay to show the activity when locked.
         return mService.mStackSupervisor.isCurrentProfileLocked(userId)
                 || topRunningActivityLocked() != null;
@@ -1182,7 +1190,7 @@
         mActivities.add(newTop);
 
         // Make sure window manager is aware of the position change.
-        mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
+        mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken);
         updateEffectiveIntent();
 
         setFrontOfTask();
@@ -1211,7 +1219,7 @@
      * be in the current task or unparented to any task.
      */
     void addActivityAtIndex(int index, ActivityRecord r) {
-        TaskRecord task = r.getTask();
+        TaskRecord task = r.getTaskRecord();
         if (task != null && task != this) {
             throw new IllegalArgumentException("Can not add r=" + " to task=" + this
                     + " current parent=" + task);
@@ -1264,17 +1272,15 @@
             mService.notifyTaskPersisterLocked(this, false);
         }
 
-        // Sync. with window manager
-        final AppWindowContainerController appController = r.getWindowContainerController();
-        if (appController != null) {
+        if (r.mAppWindowToken != null) {
             // Only attempt to move in WM if the child has a controller. It is possible we haven't
             // created controller for the activity we are starting yet.
-            mWindowContainerController.positionChildAt(appController, index);
+            mWindowContainerController.positionChildAt(r.mAppWindowToken, index);
         }
 
         // Make sure the list of display UID whitelists is updated
         // now that this record is in a new task.
-        mService.mStackSupervisor.updateUIDsPresentOnDisplay();
+        mService.mRootActivityContainer.updateUIDsPresentOnDisplay();
     }
 
     /**
@@ -1287,7 +1293,7 @@
     }
 
     boolean removeActivity(ActivityRecord r, boolean reparenting) {
-        if (r.getTask() != this) {
+        if (r.getTaskRecord() != this) {
             throw new IllegalArgumentException(
                     "Activity=" + r + " does not belong to task=" + this);
         }
@@ -1403,7 +1409,7 @@
             if (r.finishing) {
                 continue;
             }
-            if (r.realActivity.equals(newR.realActivity)) {
+            if (r.mActivityComponent.equals(newR.mActivityComponent)) {
                 // Here it is!  Now finish everything in front...
                 final ActivityRecord ret = r;
 
@@ -1561,13 +1567,13 @@
      * the index within the history at which it's found, or < 0 if not found.
      */
     final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
-        final ComponentName realActivity = r.realActivity;
+        final ComponentName realActivity = r.mActivityComponent;
         for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord candidate = mActivities.get(activityNdx);
             if (candidate.finishing) {
                 continue;
             }
-            if (candidate.realActivity.equals(realActivity)) {
+            if (candidate.mActivityComponent.equals(realActivity)) {
                 return candidate;
             }
         }
@@ -1683,9 +1689,9 @@
         // to do this for the pinned stack as the bounds are controlled by the system.
         if (!inPinnedWindowingMode()) {
             final int defaultMinSizeDp =
-                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+                    mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp;
             final ActivityDisplay display =
-                    mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+                    mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId);
             final float density =
                     (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
             final int defaultMinSize = (int) (defaultMinSizeDp * density);
@@ -1734,9 +1740,15 @@
         final Configuration newOverrideConfig = new Configuration();
         if (bounds != null) {
             newOverrideConfig.setTo(getOverrideConfiguration());
-            mTmpRect.set(bounds);
+            if (insetBounds != null && !insetBounds.isEmpty()) {
+                mTmpRect.set(insetBounds);
+                setDisplayedBounds(bounds);
+            } else {
+                mTmpRect.set(bounds);
+                setDisplayedBounds(null);
+            }
             adjustForMinimalTaskDimensions(mTmpRect);
-            computeOverrideConfiguration(newOverrideConfig, mTmpRect, insetBounds,
+            computeOverrideConfiguration(newOverrideConfig, mTmpRect,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
 
@@ -1784,16 +1796,23 @@
                 setLastNonFullscreenBounds(currentBounds);
             }
             setBounds(null);
+            setDisplayedBounds(null);
             newConfig.unset();
         } else {
-            mTmpRect.set(bounds);
+            if (insetBounds != null && !insetBounds.isEmpty()) {
+                mTmpRect.set(insetBounds);
+                setDisplayedBounds(bounds);
+            } else {
+                mTmpRect.set(bounds);
+                setDisplayedBounds(null);
+            }
             adjustForMinimalTaskDimensions(mTmpRect);
             setBounds(mTmpRect);
 
             if (mStack == null || persistBounds) {
                 setLastNonFullscreenBounds(getOverrideBounds());
             }
-            computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
+            computeOverrideConfiguration(newConfig, mTmpRect,
                     mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
         }
         onOverrideConfigurationChanged(newConfig);
@@ -1849,11 +1868,44 @@
         mService.mStackSupervisor.mLaunchParamsPersister.saveTask(this);
     }
 
+    /**
+     * Displayed bounds are used to set where the task is drawn at any given time. This is
+     * separate from its actual bounds so that the app doesn't see any meaningful configuration
+     * changes during transitionary periods.
+     */
+    void setDisplayedBounds(Rect bounds) {
+        if (bounds == null) {
+            mDisplayedBounds.setEmpty();
+        } else {
+            mDisplayedBounds.set(bounds);
+        }
+        final TaskWindowContainerController controller = getWindowContainerController();
+        if (controller != null && controller.mContainer != null) {
+            controller.mContainer.setOverrideDisplayedBounds(
+                    mDisplayedBounds.isEmpty() ? null : mDisplayedBounds);
+        }
+    }
+
+    /**
+     * Gets the current overridden displayed bounds. These will be empty if the task is not
+     * currently overriding where it is displayed.
+     */
+    Rect getDisplayedBounds() {
+        return mDisplayedBounds;
+    }
+
+    /**
+     * @return {@code true} if this has overridden displayed bounds.
+     */
+    boolean hasDisplayedBounds() {
+        return !mDisplayedBounds.isEmpty();
+    }
+
     /** Clears passed config and fills it with new override values. */
     // TODO(b/36505427): TaskRecord.computeOverrideConfiguration() is a utility method that doesn't
     // depend on task or stacks, but uses those object to get the display to base the calculation
     // on. Probably best to centralize calculations like this in ConfigurationContainer.
-    void computeOverrideConfiguration(Configuration config, Rect bounds, Rect insetBounds,
+    void computeOverrideConfiguration(Configuration config, Rect bounds,
             boolean overrideWidth, boolean overrideHeight) {
         mTmpNonDecorBounds.set(bounds);
         mTmpStableBounds.set(bounds);
@@ -1865,7 +1917,7 @@
 
         if (mStack != null) {
             final StackWindowController stackController = mStack.getWindowContainerController();
-            stackController.adjustConfigurationForBounds(bounds, insetBounds,
+            stackController.adjustConfigurationForBounds(bounds,
                     mTmpNonDecorBounds, mTmpStableBounds, overrideWidth, overrideHeight, density,
                     config, parentConfig, getWindowingMode());
         } else {
@@ -1992,7 +2044,7 @@
                 ? reuseActivitiesReport.base.intent.getComponent()
                 : null;
         info.topActivity = reuseActivitiesReport.top != null
-                ? reuseActivitiesReport.top.realActivity
+                ? reuseActivitiesReport.top.mActivityComponent
                 : null;
         info.origActivity = origActivity;
         info.realActivity = realActivity;
@@ -2043,7 +2095,7 @@
             pw.println(origActivity.flattenToShortString());
         }
         if (realActivity != null) {
-            pw.print(prefix); pw.print("realActivity=");
+            pw.print(prefix); pw.print("mActivityComponent=");
             pw.println(realActivity.flattenToShortString());
         }
         if (autoRemoveRecents || isPersistable || !isActivityTypeStandard() || numFullscreen != 0) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index a7b0272..9a56606 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -32,6 +32,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -65,6 +66,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.view.InsetsState;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -141,6 +143,7 @@
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
         final Rect tmpStableInsets = new Rect();
+        final InsetsState mTmpInsetsState = new InsetsState();
         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
         int backgroundColor = WHITE;
         int statusBarColor = 0;
@@ -201,7 +204,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
-                    tmpRect, tmpCutout, null);
+                    tmpRect, tmpCutout, null, mTmpInsetsState);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -217,7 +220,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpCutout, tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -312,7 +315,7 @@
         // Keep a reference to it such that it doesn't get destroyed when finalized.
         mChildSurfaceControl = new SurfaceControl.Builder(session)
                 .setName(mTitle + " - task-snapshot-surface")
-                .setSize(buffer.getWidth(), buffer.getHeight())
+                .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
                 .build();
         Surface surface = new Surface();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 64f4ba5..b16e184c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -184,9 +184,15 @@
         // Update bounds of containing tasks.
         for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
             final Task task = mChildren.get(taskNdx);
-            task.setBounds(taskBounds.get(task.mTaskId), false /* forced */);
-            task.setTempInsetBounds(taskTempInsetBounds != null ?
-                    taskTempInsetBounds.get(task.mTaskId) : null);
+            final Rect insetBounds =
+                    taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) : null;
+            if (insetBounds != null) {
+                task.setBounds(insetBounds);
+                task.setOverrideDisplayedBounds(taskBounds.get(task.mTaskId));
+            } else {
+                task.setBounds(taskBounds.get(task.mTaskId));
+                task.setOverrideDisplayedBounds(null);
+            }
         }
         return true;
     }
@@ -248,7 +254,6 @@
         getRawBounds(mTmpRect);
         final Rect stackBounds = getBounds();
         getPendingTransaction()
-                .setSize(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
                 .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height())
                 .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left,
                         mTmpRect.top - stackBounds.top);
@@ -451,7 +456,7 @@
             final int newDockSide = getDockSide(parentConfig, inOutBounds);
             // Update the dock create mode and clear the dock create bounds, these
             // might change after a rotation and the original values will be invalid.
-            mService.setDockedStackCreateStateLocked(
+            mWmService.setDockedStackCreateStateLocked(
                     (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
                             ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
                             : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
@@ -524,7 +529,7 @@
         mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight,
                 displayCutout, outBounds);
         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
-                mService.mContext.getResources(), displayWidth, displayHeight,
+                mWmService.mContext.getResources(), displayWidth, displayHeight,
                 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
                 getDockSide(), isMinimizedDockAndHomeStackResizable());
         final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
@@ -595,7 +600,7 @@
     private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
             boolean addingNew) {
         final boolean canShowTask =
-                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
+                showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
 
         final int stackSize = mChildren.size();
         int minPosition = 0;
@@ -629,7 +634,7 @@
             final Task tmpTask = mChildren.get(minPosition);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
-                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
+                            || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
             if (canShowTmpTask) {
                 break;
             }
@@ -648,7 +653,7 @@
             final Task tmpTask = mChildren.get(maxPosition);
             final boolean canShowTmpTask =
                     tmpTask.showForAllUsers()
-                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
+                            || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
             if (!canShowTmpTask) {
                 break;
             }
@@ -729,7 +734,7 @@
 
             // We multiply by two to match the client logic for converting view elevation
             // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
-            return (int)Math.ceil(mService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
+            return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
                     displayMetrics) * 2);
         }
         return 0;
@@ -740,7 +745,7 @@
             return;
         }
 
-        final Rect stackBounds = getBounds();
+        final Rect stackBounds = getDisplayedBounds();
         int width = stackBounds.width();
         int height = stackBounds.height();
 
@@ -751,7 +756,6 @@
         if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
             return;
         }
-        transaction.setSize(mSurfaceControl, width, height);
         transaction.setWindowCrop(mSurfaceControl, width, height);
         mLastSurfaceSize.set(width, height);
     }
@@ -793,7 +797,7 @@
 
         if (dockedBounds == null || dockedBounds.isEmpty()) {
             // Calculate the primary docked bounds.
-            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
+            final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode
                     == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
             getStackDockedModeBounds(parentConfig,
                     true /* primary */, outStackBounds, dockedBounds,
@@ -858,8 +862,8 @@
 
         outBounds.set(displayRect);
         if (primary) {
-            if (mService.mDockedStackCreateBounds != null) {
-                outBounds.set(mService.mDockedStackCreateBounds);
+            if (mWmService.mDockedStackCreateBounds != null) {
+                outBounds.set(mWmService.mDockedStackCreateBounds);
                 return;
             }
 
@@ -870,7 +874,7 @@
             mDisplayContent.getDisplayPolicy().getStableInsetsLw(
                     parentConfig.windowConfiguration.getRotation(),
                     displayRect.width(), displayRect.height(), displayCutout, mTmpRect2);
-            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
+            final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(),
                     displayRect.width(),
                     displayRect.height(),
                     dockDividerWidth,
@@ -915,7 +919,7 @@
             throw new IllegalStateException("Not a docked stack=" + this);
         }
 
-        mService.mDockedStackCreateBounds = null;
+        mWmService.mDockedStackCreateBounds = null;
 
         final Rect bounds = new Rect();
         final Rect tempBounds = new Rect();
@@ -957,7 +961,7 @@
         }
 
         mDisplayContent = null;
-        mService.mWindowPlacerLocked.requestTraversal();
+        mWmService.mWindowPlacerLocked.requestTraversal();
     }
 
     void resetAnimationBackgroundAnimator() {
@@ -979,7 +983,7 @@
         int top = mChildren.size();
         for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
             Task task = mChildren.get(taskNdx);
-            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
+            if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
                 mChildren.remove(taskNdx);
                 mChildren.add(task);
                 --top;
@@ -1048,7 +1052,7 @@
             }
             mAdjustedForIme = false;
             updateAdjustedBounds();
-            mService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
+            mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f);
         } else {
             mImeGoingAway |= mAdjustedForIme;
         }
@@ -1181,7 +1185,7 @@
         }
 
         if (dockSide == DOCKED_TOP) {
-            mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+            mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
             int topInset = mTmpRect.top;
             mTmpAdjustedBounds.set(getRawBounds());
             mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount)
@@ -1217,7 +1221,7 @@
         }
 
         if (dockSide == DOCKED_TOP) {
-            mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
+            mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
             int topInset = mTmpRect.top;
             return getRawBounds().bottom - topInset;
         } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
@@ -1242,11 +1246,11 @@
         }
         setAdjustedBounds(mTmpAdjustedBounds);
 
-        final boolean isImeTarget = (mService.getImeFocusStackLocked() == this);
+        final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this);
         if (mAdjustedForIme && adjust && !isImeTarget) {
             final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
                     * IME_ADJUST_DIM_AMOUNT;
-            mService.setResizeDimLayer(true, getWindowingMode(), alpha);
+            mWmService.setResizeDimLayer(true, getWindowingMode(), alpha);
         }
     }
 
@@ -1512,14 +1516,14 @@
 
     public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             if (mCancelCurrentBoundsAnimation) {
                 return false;
             }
         }
 
         try {
-            mService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
+            mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds);
         } catch (RemoteException e) {
             // I don't believe you.
         }
@@ -1537,7 +1541,7 @@
     @Override  // AnimatesBounds
     public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
         // Hold the lock since this is called from the BoundsAnimator running on the UiThread
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             if (!isAttached()) {
                 // Don't run the animation if the stack is already detached
                 return false;
@@ -1557,7 +1561,7 @@
 
         if (inPinnedWindowingMode()) {
             try {
-                mService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
+                mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted();
             } catch (RemoteException e) {
                 // I don't believe you...
             }
@@ -1600,9 +1604,9 @@
             }
 
             try {
-                mService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
+                mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
                 if (moveToFullscreen) {
-                    mService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
+                    mWmService.mActivityTaskManager.moveTasksToFullscreenStack(mStackId,
                             true /* onTop */);
                 }
             } catch (RemoteException e) {
@@ -1616,7 +1620,7 @@
 
     @Override
     public boolean isAttached() {
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             return mDisplayContent != null;
         }
     }
@@ -1625,19 +1629,19 @@
      * Called immediately prior to resizing the tasks at the end of the pinned stack animation.
      */
     public void onPipAnimationEndResize() {
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             mBoundsAnimating = false;
             for (int i = 0; i < mChildren.size(); i++) {
                 final Task t = mChildren.get(i);
                 t.clearPreserveNonFloatingState();
             }
-            mService.requestTraversal();
+            mWmService.requestTraversal();
         }
     }
 
     @Override
     public boolean shouldDeferStartOnMoveToFullscreen() {
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             if (!isAttached()) {
                 // Unnecessary to pause the animation because the stack is detached.
                 return false;
@@ -1756,14 +1760,6 @@
         scheduleAnimation();
     }
 
-    @Override
-    void getRelativePosition(Point outPos) {
-        super.getRelativePosition(outPos);
-        final int outset = getStackOutset();
-        outPos.x -= outset;
-        outPos.y -= outset;
-    }
-
     AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() {
         return mAnimatingAppWindowTokenRegistry;
     }
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 59b2055..ec64d2e 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,14 @@
 
 package com.android.server.wm;
 
+import static com.android.server.EventLogTags.WM_TASK_CREATED;
+import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.graphics.Rect;
@@ -24,18 +32,11 @@
 import android.os.Message;
 import android.util.EventLog;
 import android.util.Slog;
+
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.ref.WeakReference;
 
-import static com.android.server.EventLogTags.WM_TASK_CREATED;
-import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Controller for the task container. This is created by activity manager to link task records to
  * the task container they use in window manager.
@@ -103,16 +104,15 @@
         }
     }
 
-    public void positionChildAtTop(AppWindowContainerController childController) {
-        positionChildAt(childController, POSITION_TOP);
+    void positionChildAtTop(AppWindowToken aToken) {
+        positionChildAt(aToken, POSITION_TOP);
     }
 
-    public void positionChildAt(AppWindowContainerController childController, int position) {
+    void positionChildAt(AppWindowToken aToken, int position) {
         synchronized (mService.mGlobalLock) {
-            final AppWindowToken aToken = childController.mContainer;
             if (aToken == null) {
                 Slog.w(TAG_WM,
-                        "Attempted to position of non-existing app : " + childController);
+                        "Attempted to position of non-existing app");
                 return;
             }
 
diff --git a/services/core/java/com/android/server/wm/VrController.java b/services/core/java/com/android/server/wm/VrController.java
index abe40a7..3e136d35 100644
--- a/services/core/java/com/android/server/wm/VrController.java
+++ b/services/core/java/com/android/server/wm/VrController.java
@@ -187,7 +187,7 @@
         synchronized (mGlobalAmLock) {
             vrMode = record.requestedVrComponent != null;
             requestedPackage = record.requestedVrComponent;
-            userId = record.userId;
+            userId = record.mUserId;
             callingPackage = record.info.getComponentName();
 
             // Tell the VrController that a VR mode change is requested.
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 9216b66..e6ac059 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -20,19 +20,18 @@
 
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Typeface;
-import android.graphics.Paint.FontMetricsInt;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.Display;
-import android.view.Surface.OutOfResourcesException;
 import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 
 /**
  * Displays a watermark on top of the window manager's windows.
@@ -116,7 +115,7 @@
         try {
             ctrl = dc.makeOverlay()
                     .setName("WatermarkSurface")
-                    .setSize(1, 1)
+                    .setBufferSize(1, 1)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
             ctrl.setLayerStack(mDisplay.getLayerStack());
@@ -133,7 +132,7 @@
         if (mLastDW != dw || mLastDH != dh) {
             mLastDW = dw;
             mLastDH = dh;
-            mSurfaceControl.setSize(dw, dh);
+            mSurfaceControl.setBufferSize(dw, dh);
             mDrawNeeded = true;
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 449c409..b8a0739 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -260,8 +258,7 @@
             if (DEBUG_WINDOW_TRACE) {
                 Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
                         + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
-                        + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
-                        + Integer.toHexString(getPendingLayoutChanges(DEFAULT_DISPLAY)));
+                        + " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 266006d..c30cc17 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -115,7 +115,7 @@
      */
     protected final Transaction mPendingTransaction;
     protected final SurfaceAnimator mSurfaceAnimator;
-    protected final WindowManagerService mService;
+    protected final WindowManagerService mWmService;
 
     private final Point mTmpPos = new Point();
     protected final Point mLastSurfacePosition = new Point();
@@ -129,10 +129,10 @@
      */
     private boolean mCommittedReparentToAnimationLeash;
 
-    WindowContainer(WindowManagerService service) {
-        mService = service;
-        mPendingTransaction = service.mTransactionFactory.make();
-        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, service);
+    WindowContainer(WindowManagerService wms) {
+        mWmService = wms;
+        mPendingTransaction = wms.mTransactionFactory.make();
+        mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
     }
 
     @Override
@@ -515,24 +515,6 @@
         }
     }
 
-    /**
-     * Update the surface size when display changed in order to avoid children being bound by the
-     * old display size.
-     *
-     * Note that we don't want to apply this to all layers, but only limiting this to layers that
-     * don't set their own size ({@link Task}, {@link WindowState} and {@link WindowToken}).
-     */
-    void updateSurfaceSize(DisplayContent dc) {
-        if (mSurfaceControl == null) {
-            return;
-        }
-
-        final int newSurfaceSize = dc.getSurfaceSize();
-        if (mSurfaceControl.getWidth() != newSurfaceSize) {
-            getPendingTransaction().setSize(mSurfaceControl, newSurfaceSize, newSurfaceSize);
-        }
-    }
-
     void setWaitingForDrawnIfResizingChanged() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
@@ -1284,7 +1266,7 @@
             return;
         }
 
-        getRelativePosition(mTmpPos);
+        getRelativeDisplayedPosition(mTmpPos);
         if (mTmpPos.equals(mLastSurfacePosition)) {
             return;
         }
@@ -1293,12 +1275,22 @@
         mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
     }
 
-    void getRelativePosition(Point outPos) {
-        final Rect bounds = getBounds();
-        outPos.set(bounds.left, bounds.top);
+    /**
+     * Displayed bounds specify where to display this container at. It differs from bounds during
+     * certain operations (like animation or interactive dragging).
+     *
+     * @return the bounds to display this container at.
+     */
+    Rect getDisplayedBounds() {
+        return getBounds();
+    }
+
+    void getRelativeDisplayedPosition(Point outPos) {
+        final Rect dispBounds = getDisplayedBounds();
+        outPos.set(dispBounds.left, dispBounds.top);
         final WindowContainer parent = getParent();
         if (parent != null) {
-            final Rect parentBounds = parent.getBounds();
+            final Rect parentBounds = parent.getDisplayedBounds();
             outPos.offset(-parentBounds.left, -parentBounds.top);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainerListener.java b/services/core/java/com/android/server/wm/WindowContainerListener.java
index 4b3cd36..3d3d2e0 100644
--- a/services/core/java/com/android/server/wm/WindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/WindowContainerListener.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import android.content.res.Configuration;
+
 /**
  * Interface used by the owner/creator of the container to listen to changes with the container.
  * @see WindowContainerController
@@ -23,4 +25,5 @@
 public interface WindowContainerListener {
     void registerConfigurationChangeListener(ConfigurationContainerListener listener);
     void unregisterConfigurationChangeListener(ConfigurationContainerListener listener);
+    default void onInitializeOverrideConfiguration(Configuration config) {}
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 39a8465..52b24b3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -66,6 +66,7 @@
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
 
 import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
@@ -208,6 +209,7 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEventReceiver;
+import android.view.InsetsState;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
@@ -599,7 +601,6 @@
     long mDisplayFreezeTime = 0;
     int mLastDisplayFreezeDuration = 0;
     Object mLastFinishedFreezeSource = null;
-    boolean mWaitingForConfig = false;
     boolean mSwitchingUser = false;
 
     final static int WINDOWS_FREEZING_SCREENS_NONE = 0;
@@ -1112,7 +1113,8 @@
     public int addWindow(Session session, IWindow client, int seq,
             LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
+            InsetsState outInsetsState) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1460,6 +1462,7 @@
                     outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
 
             if (mInTouchMode) {
                 res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
@@ -1857,7 +1860,7 @@
             long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
-            Surface outSurface) {
+            Surface outSurface, InsetsState outInsetsState) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -1870,7 +1873,7 @@
         long origId = Binder.clearCallingIdentity();
         final int displayId;
         synchronized (mGlobalLock) {
-            WindowState win = windowForClientLocked(session, client, false);
+            final WindowState win = windowForClientLocked(session, client, false);
             if (win == null) {
                 return 0;
             }
@@ -1885,8 +1888,9 @@
 
             win.setFrameNumber(frameNumber);
 
-            if (!mWaitingForConfig) {
-                win.finishSeamlessRotation();
+            final DisplayContent dc = win.getDisplayContent();
+            if (!dc.mWaitingForConfig) {
+                win.finishSeamlessRotation(false /* timeout */);
             }
 
             int attrChanges = 0;
@@ -2157,6 +2161,7 @@
                     outStableInsets, outOutsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
             outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+            outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));
             if (localLOGV) Slog.v(
                 TAG_WM, "Relayout given client " + client.asBinder()
                 + ", requestedWidth=" + requestedWidth
@@ -2438,7 +2443,7 @@
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
             displayContent.computeScreenConfiguration(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
-                mWaitingForConfig = true;
+                displayContent.mWaitingForConfig = true;
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 displayContent.getDisplayPolicy().selectRotationAnimationLw(anim);
@@ -2451,9 +2456,10 @@
         return config;
     }
 
-    void setNewDisplayOverrideConfiguration(Configuration overrideConfig, DisplayContent dc) {
-        if (mWaitingForConfig) {
-            mWaitingForConfig = false;
+    void setNewDisplayOverrideConfiguration(Configuration overrideConfig,
+            @NonNull DisplayContent dc) {
+        if (dc.mWaitingForConfig) {
+            dc.mWaitingForConfig = false;
             mLastFinishedFreezeSource = "new-config";
         }
 
@@ -2474,26 +2480,36 @@
     @Override
     public void overridePendingAppTransitionMultiThumbFuture(
             IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback callback,
-            boolean scaleUp) {
+            boolean scaleUp, int displayId) {
         synchronized (mGlobalLock) {
-            // TODO(multi-display): sysui using this api only support default display.
-            mRoot.getDisplayContent(DEFAULT_DISPLAY)
-                    .mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                Slog.w(TAG, "Attempted to call overridePendingAppTransitionMultiThumbFuture"
+                        + " for the display " + displayId + " that does not exist.");
+                return;
+            }
+            displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(specsFuture,
                     callback, scaleUp);
         }
     }
 
     @Override
-    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) {
+    public void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
+            int displayId) {
         if (!checkCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
                 "overridePendingAppTransitionRemote()")) {
             throw new SecurityException(
                     "Requires CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS permission");
         }
         synchronized (mGlobalLock) {
-            // TODO(multi-display): sysui using this api only support default display.
-            mRoot.getDisplayContent(DEFAULT_DISPLAY)
-                    .mAppTransition.overridePendingAppTransitionRemote(remoteAnimationAdapter);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            if (displayContent == null) {
+                Slog.w(TAG, "Attempted to call overridePendingAppTransitionRemote"
+                        + " for the display " + displayId + " that does not exist.");
+                return;
+            }
+            displayContent.mAppTransition.overridePendingAppTransitionRemote(
+                    remoteAnimationAdapter);
         }
     }
 
@@ -3576,6 +3592,17 @@
         }
     }
 
+    void setRotateForApp(int displayId, boolean enabled) {
+        synchronized (mGlobalLock) {
+            final DisplayContent display = mRoot.getDisplayContent(displayId);
+            if (display == null) {
+                Slog.w(TAG, "Trying to set rotate for app for a missing display.");
+                return;
+            }
+            display.getDisplayRotation().setFixedToUserRotation(enabled);
+        }
+    }
+
     @Override
     public void freezeRotation(int rotation) {
         freezeDisplayRotation(Display.DEFAULT_DISPLAY, rotation);
@@ -4181,13 +4208,11 @@
                 // placement to unfreeze the display since we froze it when the rotation was updated
                 // in DisplayContent#updateRotationUnchecked.
                 synchronized (mGlobalLock) {
-                    if (mWaitingForConfig) {
-                        mWaitingForConfig = false;
+                    final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                    if (dc != null && dc.mWaitingForConfig) {
+                        dc.mWaitingForConfig = false;
                         mLastFinishedFreezeSource = "config-unchanged";
-                        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                        if (dc != null) {
-                            dc.setLayoutNeeded();
-                        }
+                        dc.setLayoutNeeded();
                         mWindowPlacerLocked.performSurfacePlacement();
                     }
                 }
@@ -5136,7 +5161,7 @@
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
-            mWaitingForConfig = true;
+            displayContent.mWaitingForConfig = true;
             startFreezingDisplayLocked(0 /* exitAnim */,
                     0 /* enterAnim */, displayContent);
             displayContent.sendNewConfiguration();
@@ -5384,7 +5409,7 @@
 
             displayContent.updateDisplayInfo();
             screenRotationAnimation = new ScreenRotationAnimation(mContext, displayContent,
-                    displayContent.getDisplayRotation().isDefaultOrientationForced(), isSecure,
+                    displayContent.getDisplayRotation().isFixedToUserRotation(), isSecure,
                     this);
             mAnimator.setScreenRotationAnimationLocked(mFrozenDisplayId,
                     screenRotationAnimation);
@@ -5396,23 +5421,24 @@
             return;
         }
 
-        final DisplayContent dc = mRoot.getDisplayContent(mFrozenDisplayId);
-        if (mWaitingForConfig || mAppsFreezingScreen > 0
+        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
+        final boolean waitingForConfig = displayContent != null && displayContent.mWaitingForConfig;
+        final int numOpeningApps = displayContent != null ? displayContent.mOpeningApps.size() : 0;
+        if (waitingForConfig || mAppsFreezingScreen > 0
                 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
-                || mClientFreezingScreen || (dc != null && !dc.mOpeningApps.isEmpty())) {
+                || mClientFreezingScreen || numOpeningApps > 0) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
-                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + mWaitingForConfig
+                "stopFreezingDisplayLocked: Returning mWaitingForConfig=" + waitingForConfig
                 + ", mAppsFreezingScreen=" + mAppsFreezingScreen
                 + ", mWindowsFreezingScreen=" + mWindowsFreezingScreen
                 + ", mClientFreezingScreen=" + mClientFreezingScreen
-                + ", mOpeningApps.size()=" + (dc != null ? dc.mOpeningApps.size() : 0));
+                + ", mOpeningApps.size()=" + numOpeningApps);
             return;
         }
 
         if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
                 "stopFreezingDisplayLocked: Unfreezing now");
 
-        final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId);
 
         // We must make a local copy of the displayId as it can be potentially overwritten later on
         // in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
@@ -5615,22 +5641,12 @@
         }
     }
 
-    // TODO: Make the callers use getNavBarPosition(int) only.
-    /**
-     * Used by SystemUI and shared SystemUI libraries.
-     * @see DisplayPolicy#getNavBarPosition()
-     */
-    @Override
-    @WindowManagerPolicy.NavigationBarPosition
-    public int getNavBarPosition() {
-        return getNavBarPosition(Display.DEFAULT_DISPLAY);
-    }
-
     /**
      * Used by ActivityManager to determine where to position an app with aspect ratio shorter then
      * the screen is.
      * @see DisplayPolicy#getNavBarPosition()
      */
+    @Override
     @WindowManagerPolicy.NavigationBarPosition
     public int getNavBarPosition(int displayId) {
         synchronized (mGlobalLock) {
@@ -5640,7 +5656,7 @@
             if (displayContent == null) {
                 Slog.w(TAG, "getNavBarPosition with invalid displayId=" + displayId
                         + " callers=" + Debug.getCallers(3));
-                return -1;
+                return NAV_BAR_INVALID;
             }
             displayContent.performLayout(false /* initial */,
                     false /* updateInputWindows */);
@@ -5707,8 +5723,14 @@
     }
 
     @Override
-    public boolean hasNavigationBar() {
-        return mPolicy.hasNavigationBar();
+    public boolean hasNavigationBar(int displayId) {
+        synchronized (mGlobalLock) {
+            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            if (dc == null) {
+                return false;
+            }
+            return dc.getDisplayPolicy().hasNavigationBar();
+        }
     }
 
     @Override
@@ -6033,7 +6055,6 @@
                     pw.print(" windows="); pw.print(mWindowsFreezingScreen);
                     pw.print(" client="); pw.print(mClientFreezingScreen);
                     pw.print(" apps="); pw.print(mAppsFreezingScreen);
-                    pw.print(" waitingForConfig="); pw.println(mWaitingForConfig);
             final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
             pw.print("  mRotation="); pw.print(defaultDisplayContent.getRotation());
                     pw.print(" mAltOrientation=");
@@ -6042,6 +6063,9 @@
                     pw.print(defaultDisplayContent.getLastWindowForcedOrientation());
                     pw.print(" mLastOrientation=");
                             pw.println(defaultDisplayContent.getLastOrientation());
+                    pw.print(" waitingForConfig=");
+                            pw.println(defaultDisplayContent.mWaitingForConfig);
+
             pw.print("  Animation settings: disabled="); pw.print(mAnimationsDisabled);
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
@@ -7424,4 +7448,29 @@
             }
         }
     }
+
+    @Override
+    public void reparentDisplayContent(int displayId, IBinder surfaceControlHandle) {
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            throw new IllegalArgumentException(
+                    "Can't reparent display for non-existent displayId: " + displayId);
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        final int displayOwnerUid = display.getOwnerUid();
+        if (callingUid != displayOwnerUid) {
+            throw new SecurityException("Only owner of the display can reparent surfaces to it.");
+        }
+
+        synchronized (mGlobalLock) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                displayContent.reparentDisplayContent(surfaceControlHandle);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index bf77ba8..6865ce3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -76,6 +76,8 @@
                             getNextArgRequired());
                 case "set-user-rotation":
                     return runSetDisplayUserRotation(pw);
+                case "set-fix-to-user-rotation":
+                    return runSetFixToUserRotation(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -297,6 +299,32 @@
         }
     }
 
+    private int runSetFixToUserRotation(PrintWriter pw) {
+        int displayId = Display.DEFAULT_DISPLAY;
+        String arg = getNextArgRequired();
+        if ("-d".equals(arg)) {
+            displayId = Integer.parseInt(getNextArgRequired());
+            arg = getNextArgRequired();
+        }
+
+        final boolean enabled;
+        switch (arg) {
+            case "enabled":
+                enabled = true;
+                break;
+            case "disabled":
+                enabled = false;
+                break;
+            default:
+                getErrPrintWriter().println("Error: expecting enabled or disabled, but we get "
+                        + arg);
+                return -1;
+        }
+
+        mInternal.setRotateForApp(displayId, enabled);
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -316,6 +344,8 @@
         pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
         pw.println("  set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]");
         pw.println("    Set user rotation mode and user rotation.");
+        pw.println("  set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]");
+        pw.println("    Enable or disable rotating display for app requested orientation.");
         if (!IS_USER) {
             pw.println("  tracing (start | stop)");
             pw.println("    Start or stop window tracing.");
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 484bd8c..2dec4dd 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -443,7 +443,7 @@
             for (int i = 0; i < activities.size(); i++) {
                 final ActivityRecord r = activities.get(i);
                 if (!r.finishing && r.isInStackLocked()) {
-                    r.getStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
+                    r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
                             null, "finish-heavy", true);
                 }
             }
@@ -513,7 +513,7 @@
             }
             ActivityRecord hist = mActivities.get(0);
             intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP, hist.packageName);
-            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTask().taskId);
+            intent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK, hist.getTaskRecord().taskId);
         }
     }
 
@@ -524,7 +524,7 @@
                 // Don't kill process(es) that has an activity not stopped.
                 return false;
             }
-            final TaskRecord otherTask = activity.getTask();
+            final TaskRecord otherTask = activity.getTaskRecord();
             if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
                 // Don't kill process(es) that has an activity in a different task that is
                 // also in recents.
@@ -557,7 +557,7 @@
                 continue;
             }
 
-            final TaskRecord task = r.getTask();
+            final TaskRecord task = r.getTaskRecord();
             if (task != null) {
                 if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
                         + " from " + r);
@@ -600,7 +600,7 @@
                 }
                 if (r.visible) {
                     callback.onVisibleActivity();
-                    final TaskRecord task = r.getTask();
+                    final TaskRecord task = r.getTaskRecord();
                     if (task != null && minTaskLayer > 0) {
                         final int layer = task.mLayerRank;
                         if (layer >= 0 && minTaskLayer > layer) {
@@ -755,9 +755,9 @@
             return;
         }
         final ActivityDisplay activityDisplay =
-                mAtm.mStackSupervisor.getActivityDisplay(mDisplayId);
+                mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId);
         if (activityDisplay != null) {
-            mAtm.mStackSupervisor.getActivityDisplay(
+            mAtm.mRootActivityContainer.getActivityDisplay(
                     mDisplayId).unregisterConfigurationChangeListener(this);
         }
         mDisplayId = INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 567b583..5cc3623 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -143,6 +143,7 @@
 import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
 
 import android.annotation.CallSuper;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -180,6 +181,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.View;
@@ -577,10 +579,12 @@
      */
     private boolean mIsDimming = false;
 
+    private @Nullable InsetsSourceProvider mInsetProvider;
+
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
 
-    void seamlesslyRotateIfAllowed(Transaction transaction, int oldRotation, int rotation,
-            boolean requested) {
+    void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
+            @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
         if (!isVisibleNow() || mIsWallpaper) {
             return;
@@ -593,16 +597,16 @@
         if (mForceSeamlesslyRotate || requested) {
             mPendingSeamlessRotate = new SeamlessRotator(oldRotation, rotation, getDisplayInfo());
             mPendingSeamlessRotate.unrotate(transaction, this);
-            mService.markForSeamlessRotation(this, true);
+            mWmService.markForSeamlessRotation(this, true);
         }
     }
 
-    void finishSeamlessRotation() {
+    void finishSeamlessRotation(boolean timeout) {
         if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this);
+            mPendingSeamlessRotate.finish(this, timeout);
             mFinishSeamlessRotateFrameNumber = getFrameNumber();
             mPendingSeamlessRotate = null;
-            mService.markForSeamlessRotation(this, false);
+            mWmService.markForSeamlessRotation(this, false);
         }
     }
 
@@ -646,8 +650,8 @@
         mAttrs.copyFrom(a);
         mLastSurfaceInsets.set(mAttrs.surfaceInsets);
         mViewVisibility = viewVisibility;
-        mPolicy = mService.mPolicy;
-        mContext = mService.mContext;
+        mPolicy = mWmService.mPolicy;
+        mContext = mWmService.mContext;
         DeathRecipient deathRecipient = new DeathRecipient();
         mSeq = seq;
         mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
@@ -786,6 +790,18 @@
     }
 
     @Override
+    public Rect getDisplayedBounds() {
+        final Task task = getTask();
+        if (task != null) {
+            Rect bounds = task.getOverrideDisplayedBounds();
+            if (!bounds.isEmpty()) {
+                return bounds;
+            }
+        }
+        return super.getDisplayedBounds();
+    }
+
+    @Override
     public void computeFrameLw() {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
@@ -801,16 +817,7 @@
         final boolean windowsAreFloating = task != null && task.isFloating();
         final DisplayContent dc = getDisplayContent();
 
-        // If the task has temp inset bounds set, we have to make sure all its windows uses
-        // the temp inset frame. Otherwise different display frames get applied to the main
-        // window and the child window, making them misaligned.
-        // Otherwise we need to clear the inset frame, to avoid using a stale frame after leaving
-        // multi window mode.
-        if (task != null && isInMultiWindowMode()) {
-            task.getTempInsetBounds(mInsetFrame);
-        } else {
-            mInsetFrame.setEmpty();
-        }
+        mInsetFrame.set(getBounds());
 
         // Denotes the actual frame used to calculate the insets and to perform the layout. When
         // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
@@ -830,7 +837,7 @@
             layoutXDiff = 0;
             layoutYDiff = 0;
         } else {
-            getBounds(mWindowFrames.mContainingFrame);
+            mWindowFrames.mContainingFrame.set(getDisplayedBounds());
             if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
 
                 // If the bounds are frozen, we still want to translate the window freely and only
@@ -841,7 +848,7 @@
                 mWindowFrames.mContainingFrame.bottom =
                         mWindowFrames.mContainingFrame.top + frozen.height();
             }
-            final WindowState imeWin = mService.mRoot.getCurrentInputMethodWindow();
+            final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
             // IME is up and obscuring this window. Adjust the window position so it is visible.
             if (imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget()) {
                 if (inFreeformWindowingMode() && mWindowFrames.mContainingFrame.bottom
@@ -880,14 +887,9 @@
 
             layoutDisplayFrame = new Rect(mWindowFrames.mDisplayFrame);
             mWindowFrames.mDisplayFrame.set(mWindowFrames.mContainingFrame);
-            layoutXDiff =
-                    !mInsetFrame.isEmpty() ? mInsetFrame.left - mWindowFrames.mContainingFrame.left
-                            : 0;
-            layoutYDiff =
-                    !mInsetFrame.isEmpty() ? mInsetFrame.top - mWindowFrames.mContainingFrame.top
-                            : 0;
-            layoutContainingFrame =
-                    !mInsetFrame.isEmpty() ? mInsetFrame : mWindowFrames.mContainingFrame;
+            layoutXDiff = mInsetFrame.left - mWindowFrames.mContainingFrame.left;
+            layoutYDiff = mInsetFrame.top - mWindowFrames.mContainingFrame.top;
+            layoutContainingFrame = mInsetFrame;
             mTmpRect.set(0, 0, dc.getDisplayInfo().logicalWidth, dc.getDisplayInfo().logicalHeight);
             subtractInsets(mWindowFrames.mDisplayFrame, layoutContainingFrame, layoutDisplayFrame,
                     mTmpRect);
@@ -1199,7 +1201,7 @@
             }
 
             updateLastInsetValues();
-            mService.makeWindowFreezingScreenIfNeededLocked(this);
+            mWmService.makeWindowFreezingScreenIfNeededLocked(this);
 
             // If the orientation is changing, or we're starting or ending a drag resizing action,
             // then we need to hold off on unfreezing the display until this window has been
@@ -1216,9 +1218,9 @@
                     mAppToken.clearAllDrawn();
                 }
             }
-            if (!mService.mResizingWindows.contains(this)) {
+            if (!mWmService.mResizingWindows.contains(this)) {
                 if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
-                mService.mResizingWindows.add(this);
+                mWmService.mResizingWindows.add(this);
             }
         } else if (getOrientationChanging()) {
             if (isDrawnLw()) {
@@ -1226,7 +1228,7 @@
                         + this + ", surfaceController " + winAnimator.mSurfaceController);
                 setOrientationChanging(false);
                 mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
-                        - mService.mDisplayFreezeTime);
+                        - mWmService.mDisplayFreezeTime);
             }
         }
     }
@@ -1260,7 +1262,6 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        updateSurfaceSize(dc);
         super.onDisplayChanged(dc);
         // Window was not laid out for this display yet, so make sure mLayoutSeq does not match.
         if (dc != null) {
@@ -1600,7 +1601,7 @@
             // 3. WS is currently visible
             if (!runningAppAnimation && isVisibleNow) {
                 final AccessibilityController accessibilityController =
-                        mService.mAccessibilityController;
+                        mWmService.mAccessibilityController;
                 final int winTransit = TRANSIT_EXIT;
                 mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
                 //TODO (multidisplay): Magnification is supported only for the default
@@ -1622,8 +1623,8 @@
         if (isVisibleNow()) {
             mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
             //TODO (multidisplay): Magnification is supported only for the default
-            if (mService.mAccessibilityController != null && isDefaultDisplay()) {
-                mService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+            if (mWmService.mAccessibilityController != null && isDefaultDisplay()) {
+                mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
             }
             changed = true;
             if (displayContent != null) {
@@ -1641,7 +1642,7 @@
 
     @Override
     void onResize() {
-        final ArrayList<WindowState> resizingWindows = mService.mResizingWindows;
+        final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
         if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
             if (DEBUG_RESIZE) Slog.d(TAG, "onResize: Resizing " + this);
             resizingWindows.add(this);
@@ -1665,8 +1666,8 @@
 
         mLayoutNeeded = true;
         setDisplayLayoutNeeded();
-        if (!mService.mResizingWindows.contains(this)) {
-            mService.mResizingWindows.add(this);
+        if (!mWmService.mResizingWindows.contains(this)) {
+            mWmService.mResizingWindows.add(this);
         }
     }
 
@@ -1698,9 +1699,9 @@
         }
 
         //TODO (multidisplay): Accessibility supported only for the default display.
-        if (mService.mAccessibilityController != null
+        if (mWmService.mAccessibilityController != null
                 && getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {
-            mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
         }
 
         try {
@@ -1814,7 +1815,7 @@
             // we are doing this as part of processing a death note.)
         }
 
-        mService.postWindowRemoveCleanupLocked(this);
+        mWmService.postWindowRemoveCleanupLocked(this);
     }
 
     @Override
@@ -1855,7 +1856,7 @@
                     + " mWillReplaceWindow=" + mWillReplaceWindow
                     + " inPendingTransaction="
                     + (mAppToken != null ? mAppToken.inPendingTransaction : false)
-                    + " mDisplayFrozen=" + mService.mDisplayFrozen
+                    + " mDisplayFrozen=" + mWmService.mDisplayFrozen
                     + " callers=" + Debug.getCallers(6));
 
             // Visibility of the removed window. Will be used later to update orientation later on.
@@ -1890,7 +1891,7 @@
 
                     mAppDied = true;
                     setDisplayLayoutNeeded();
-                    mService.mWindowPlacerLocked.performSurfacePlacement();
+                    mWmService.mWindowPlacerLocked.performSurfacePlacement();
 
                     // Set up a replacement input channel since the app is now dead.
                     // We need to catch tapping on the dead window to restart the app.
@@ -1909,11 +1910,12 @@
                         // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
                         // any change from that is performed immediately.
                         setDisplayLayoutNeeded();
-                        mService.requestTraversal();
+                        mWmService.requestTraversal();
                     }
                     //TODO (multidisplay): Magnification is supported only for the default display.
-                    if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
-                        mService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+                    if (mWmService.mAccessibilityController != null
+                            && displayId == DEFAULT_DISPLAY) {
+                        mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
                     }
                 }
                 final boolean isAnimating = isAnimating()
@@ -1947,7 +1949,7 @@
                     displayContent.sendNewConfiguration();
                 }
             }
-            mService.updateFocusedWindowLocked(isFocused()
+            mWmService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
                             : UPDATE_FOCUS_NORMAL,
                     true /*updateInputWindows*/);
@@ -1961,9 +1963,9 @@
         setDisplayLayoutNeeded();
         // Request a focus update as this window's input channel is already gone. Otherwise
         // we could have no focused window in input manager.
-        final boolean focusChanged = mService.updateFocusedWindowLocked(
+        final boolean focusChanged = mWmService.updateFocusedWindowLocked(
                 UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
-        mService.mWindowPlacerLocked.performSurfacePlacement();
+        mWmService.mWindowPlacerLocked.performSurfacePlacement();
         if (focusChanged) {
             getDisplayContent().getInputMonitor().updateInputWindowsLw(false /*force*/);
         }
@@ -2016,7 +2018,7 @@
 
     private final class DeadWindowEventReceiver extends InputEventReceiver {
         DeadWindowEventReceiver(InputChannel inputChannel) {
-            super(inputChannel, mService.mH.getLooper());
+            super(inputChannel, mWmService.mH.getLooper());
         }
         @Override
         public void onInputEvent(InputEvent event) {
@@ -2036,7 +2038,7 @@
         InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
         mInputChannel = inputChannels[0];
         mClientChannel = inputChannels[1];
-        mInputWindowHandle.inputChannel = inputChannels[0];
+        mInputWindowHandle.token = mClient.asBinder();
         if (outInputChannel != null) {
             mClientChannel.transferTo(outInputChannel);
             mClientChannel.dispose();
@@ -2047,7 +2049,7 @@
             // Create dummy event receiver that simply reports all events as handled.
             mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
         }
-        mService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
+        mWmService.mInputManager.registerInputChannel(mInputChannel, mClient.asBinder());
     }
 
     void disposeInputChannel() {
@@ -2058,7 +2060,7 @@
 
         // unregister server channel first otherwise it complains about broken channel
         if (mInputChannel != null) {
-            mService.mInputManager.unregisterInputChannel(mInputChannel);
+            mWmService.mInputManager.unregisterInputChannel(mInputChannel);
 
             mInputChannel.dispose();
             mInputChannel = null;
@@ -2067,7 +2069,7 @@
             mClientChannel.dispose();
             mClientChannel = null;
         }
-        mInputWindowHandle.inputChannel = null;
+        mInputWindowHandle.token = null;
     }
 
     /** Returns true if the replacement window was removed. */
@@ -2166,11 +2168,12 @@
                 mTmpRect.inset(-delta, -delta);
             }
             region.set(mTmpRect);
-            cropRegionToStackBoundsIfNeeded(region);
+            region.translate(-mWindowFrames.mFrame.left, -mWindowFrames.mFrame.top);
         } else {
             // Not modal or full screen modal
             getTouchableRegion(region);
         }
+
         return flags;
     }
 
@@ -2186,13 +2189,13 @@
                 if (isFocused()) {
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                             "setAnimationLocked: setting mFocusMayChange true");
-                    mService.mFocusMayChange = true;
+                    mWmService.mFocusMayChange = true;
                 }
                 setDisplayLayoutNeeded();
                 // Window is no longer visible -- make sure if we were waiting
                 // for it to be displayed before enabling the display, that
                 // we allow the display to be enabled now.
-                mService.enableScreenIfNeededLocked();
+                mWmService.enableScreenIfNeededLocked();
             }
         }
     }
@@ -2209,8 +2212,8 @@
         // We need to turn on screen regardless of visibility.
         boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
         boolean allowTheaterMode =
-                mService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
-                        mService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
+                mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
+                        mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
                         == 0;
         boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
 
@@ -2260,8 +2263,8 @@
         // For child windows we want to use the pid for the parent window in case the the child
         // window was added from another process.
         final int pid = getParentWindow() != null ? getParentWindow().mSession.mPid : mSession.mPid;
-        mTempConfiguration.setTo(mService.mProcessConfigurations.get(
-                pid, mService.mRoot.getConfiguration()));
+        mTempConfiguration.setTo(mWmService.mProcessConfigurations.get(
+                pid, mWmService.mRoot.getConfiguration()));
         return mTempConfiguration;
     }
 
@@ -2317,13 +2320,14 @@
         public void binderDied() {
             try {
                 boolean resetSplitScreenResizing = false;
-                synchronized (mService.mGlobalLock) {
-                    final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
+                synchronized (mWmService.mGlobalLock) {
+                    final WindowState win = mWmService
+                            .windowForClientLocked(mSession, mClient, false);
                     Slog.i(TAG, "WIN DEATH: " + win);
                     if (win != null) {
                         final DisplayContent dc = getDisplayContent();
                         if (win.mAppToken != null && win.mAppToken.findMainWindow() == win) {
-                            mService.mTaskSnapshotController.onAppDied(win.mAppToken);
+                            mWmService.mTaskSnapshotController.onAppDied(win.mAppToken);
                         }
                         win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
                         if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -2347,7 +2351,7 @@
                     try {
                         // Note: this calls into ActivityManager, so we must *not* hold the window
                         // manager lock while calling this.
-                        mService.mActivityTaskManager.setSplitScreenResizing(false);
+                        mWmService.mActivityTaskManager.setSplitScreenResizing(false);
                     } catch (RemoteException e) {
                         // Local call, shouldn't return RemoteException.
                         throw e.rethrowAsRuntimeException();
@@ -2391,11 +2395,11 @@
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                 && (mAppToken == null || mAppToken.windowsAreFocusable())
-                && !canReceiveTouchInput();
+                && !cantReceiveTouchInput();
     }
 
-    /** @return true if this window desires touch events. */
-    boolean canReceiveTouchInput() {
+    /** @return false if this window desires touch events. */
+    boolean cantReceiveTouchInput() {
         return mAppToken != null && mAppToken.getTask() != null
                 && mAppToken.getTask().mStack.shouldIgnoreInput();
     }
@@ -2454,10 +2458,10 @@
             mWinAnimator.applyAnimationLocked(TRANSIT_ENTER, true);
         }
         if (requestAnim) {
-            mService.scheduleAnimationLocked();
+            mWmService.scheduleAnimationLocked();
         }
         if ((mAttrs.flags & FLAG_NOT_FOCUSABLE) == 0) {
-            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
         }
         return true;
     }
@@ -2492,18 +2496,18 @@
             // Window is no longer visible -- make sure if we were waiting
             // for it to be displayed before enabling the display, that
             // we allow the display to be enabled now.
-            mService.enableScreenIfNeededLocked();
+            mWmService.enableScreenIfNeededLocked();
             if (isFocused) {
                 if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                         "WindowState.hideLw: setting mFocusMayChange true");
-                mService.mFocusMayChange = true;
+                mWmService.mFocusMayChange = true;
             }
         }
         if (requestAnim) {
-            mService.scheduleAnimationLocked();
+            mWmService.scheduleAnimationLocked();
         }
         if (isFocused) {
-            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
         }
         return true;
     }
@@ -2565,7 +2569,7 @@
         // and add the window only if the permission was granted. Therefore, if
         // the mode is MODE_DEFAULT we want the op to succeed as the window is
         // shown.
-        final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+        final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp,
                 getOwningUid(), getOwningPackage(), true);
         if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
             setAppOpVisibilityLw(false);
@@ -2574,7 +2578,7 @@
 
     void resetAppOpsState() {
         if (mAppOp != OP_NONE && mAppOpVisibility) {
-            mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+            mWmService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
         }
     }
 
@@ -2587,13 +2591,13 @@
         if (mAppOpVisibility) {
             // There is a race between the check and the finish calls but this is fine
             // as this would mean we will get another change callback and will reconcile.
-            int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+            int mode = mWmService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
             if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
-                mService.mAppOps.finishOp(mAppOp, uid, packageName);
+                mWmService.mAppOps.finishOp(mAppOp, uid, packageName);
                 setAppOpVisibilityLw(false);
             }
         } else {
-            final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
+            final int mode = mWmService.mAppOps.startOpNoThrow(mAppOp, uid, packageName, true);
             if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
                 setAppOpVisibilityLw(true);
             }
@@ -2614,7 +2618,7 @@
                 // in wake lock statistics.  So in particular, we don't want to include the
                 // window's hash code as in toString().
                 final CharSequence tag = getWindowTag();
-                mDrawLock = mService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
+                mDrawLock = mWmService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
                 mDrawLock.setReferenceCounted(false);
                 mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
             }
@@ -2699,10 +2703,10 @@
         mAppFreezing = false;
 
         if (mHasSurface && !getOrientationChanging()
-                && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
+                && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
             setOrientationChanging(true);
-            mService.mRoot.mOrientationChangeComplete = false;
+            mWmService.mRoot.mOrientationChangeComplete = false;
         }
         mLastFreezeDuration = 0;
         setDisplayLayoutNeeded();
@@ -2792,7 +2796,7 @@
         }
 
         return win.mShowToOwnerOnly
-                && !mService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
+                && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
     }
 
     private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -2802,25 +2806,32 @@
     }
 
     void getTouchableRegion(Region outRegion) {
+        if (inPinnedWindowingMode() && !isFocused()) {
+            outRegion.setEmpty();
+            return;
+        }
+
         final Rect frame = mWindowFrames.mFrame;
         switch (mTouchableInsets) {
             default:
             case TOUCHABLE_INSETS_FRAME:
                 outRegion.set(frame);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_CONTENT:
                 applyInsets(outRegion, frame, mGivenContentInsets);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_VISIBLE:
                 applyInsets(outRegion, frame, mGivenVisibleInsets);
+                outRegion.translate(-frame.left, -frame.top);
                 break;
             case TOUCHABLE_INSETS_REGION: {
                 outRegion.set(mGivenTouchableRegion);
-                outRegion.translate(frame.left, frame.top);
                 break;
             }
         }
-        cropRegionToStackBoundsIfNeeded(outRegion);
+        outRegion.translate(mAttrs.surfaceInsets.left, mAttrs.surfaceInsets.top);
     }
 
     private void cropRegionToStackBoundsIfNeeded(Region region) {
@@ -2884,7 +2895,7 @@
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mWindowFrames.mCompatFrame);
             final MergedConfiguration mergedConfiguration =
-                    new MergedConfiguration(mService.mRoot.getConfiguration(),
+                    new MergedConfiguration(mWmService.mRoot.getConfiguration(),
                     getMergedOverrideConfiguration());
 
             setLastReportedMergedConfiguration(mergedConfiguration);
@@ -2905,7 +2916,7 @@
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
-                mService.mH.post(new Runnable() {
+                mWmService.mH.post(new Runnable() {
                     @Override
                     public void run() {
                         try {
@@ -2924,8 +2935,8 @@
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
-            if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
-                mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+            if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+                mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
             }
 
             mWindowFrames.resetInsetsChanged();
@@ -2934,16 +2945,39 @@
         } catch (RemoteException e) {
             setOrientationChanging(false);
             mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
-                    - mService.mDisplayFreezeTime);
+                    - mWmService.mDisplayFreezeTime);
             // We are assuming the hosting process is dead or in a zombie state.
             Slog.w(TAG, "Failed to report 'resized' to the client of " + this
                     + ", removing this window.");
-            mService.mPendingRemove.add(this);
-            mService.mWindowPlacerLocked.requestTraversal();
+            mWmService.mPendingRemove.add(this);
+            mWmService.mWindowPlacerLocked.requestTraversal();
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
+    /**
+     * Called when the insets state changed.
+     */
+    void notifyInsetsChanged() {
+        try {
+            mClient.insetsChanged(
+                    getDisplayContent().getInsetsStateController().getInsetsForDispatch(this));
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver inset state change", e);
+        }
+    }
+
+    void notifyInsetsControlChanged() {
+        final InsetsStateController stateController =
+                getDisplayContent().getInsetsStateController();
+        try {
+            mClient.insetsControlChanged(stateController.getInsetsForDispatch(this),
+                    stateController.getControlsForDispatch(this));
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver inset state change", e);
+        }
+    }
+
     Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
@@ -2988,7 +3022,7 @@
     }
 
     public void registerFocusObserver(IWindowFocusObserver observer) {
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             if (mFocusCallbacks == null) {
                 mFocusCallbacks = new RemoteCallbackList<IWindowFocusObserver>();
             }
@@ -2997,7 +3031,7 @@
     }
 
     public void unregisterFocusObserver(IWindowFocusObserver observer) {
-        synchronized (mService.mGlobalLock) {
+        synchronized (mWmService.mGlobalLock) {
             if (mFocusCallbacks != null) {
                 mFocusCallbacks.unregister(observer);
             }
@@ -3070,7 +3104,7 @@
     @Override
     void setWaitingForDrawnIfResizingChanged() {
         if (isDragResizeChanged()) {
-            mService.mWaitingForDrawn.add(this);
+            mWmService.mWaitingForDrawn.add(this);
         }
         super.setWaitingForDrawnIfResizingChanged();
     }
@@ -3559,7 +3593,7 @@
         if (dc != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
             dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
             dc.setLayoutNeeded();
-            mService.mWindowPlacerLocked.requestTraversal();
+            mWmService.mWindowPlacerLocked.requestTraversal();
         }
 
         for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -3657,7 +3691,7 @@
 
         logPerformShow("Showing ");
 
-        mService.enableScreenIfNeededLocked();
+        mWmService.enableScreenIfNeededLocked();
         mWinAnimator.applyEnterAnimationLocked();
 
         // Force the show in the next prepareSurfaceLocked() call.
@@ -3665,7 +3699,7 @@
         if (DEBUG_ANIM) Slog.v(TAG,
                 "performShowLocked: mDrawState=HAS_DRAWN in " + this);
         mWinAnimator.mDrawState = HAS_DRAWN;
-        mService.scheduleAnimationLocked();
+        mWmService.scheduleAnimationLocked();
 
         if (mHidden) {
             mHidden = false;
@@ -3944,7 +3978,7 @@
 
         if (mWinAnimator.mEnteringAnimation) {
             mWinAnimator.mEnteringAnimation = false;
-            mService.requestTraversal();
+            mWmService.requestTraversal();
             // System windows don't have an activity and an app token as a result, but need a way
             // to be informed about their entrance animation end.
             if (mAppToken == null) {
@@ -3960,8 +3994,8 @@
         }
 
         //TODO (multidisplay): Accessibility is supported only for the default display.
-        if (mService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
-            mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+        if (mWmService.mAccessibilityController != null && getDisplayId() == DEFAULT_DISPLAY) {
+            mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
         }
 
         if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -3987,10 +4021,10 @@
             mAppToken.destroySurfaces();
         } else {
             if (hasSurface) {
-                mService.mDestroySurface.add(this);
+                mWmService.mDestroySurface.add(this);
             }
             if (mRemoveOnExit) {
-                mService.mPendingRemove.add(this);
+                mWmService.mPendingRemove.add(this);
                 mRemoveOnExit = false;
             }
         }
@@ -4021,7 +4055,7 @@
             }
             if (mDestroying) {
                 mDestroying = false;
-                mService.mDestroySurface.remove(this);
+                mWmService.mDestroySurface.remove(this);
                 didSomething = true;
             }
         }
@@ -4050,7 +4084,7 @@
             if (displayContent != null) {
                 displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                 if (DEBUG_LAYOUT_REPEATS) {
-                    mService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
+                    mWmService.mWindowPlacerLocked.debugLayoutRepeats("hideWallpaperWindow " + this,
                             displayContent.pendingLayoutChanges);
                 }
             }
@@ -4265,7 +4299,7 @@
         }
         if (mDestroying) {
             mDestroying = false;
-            mService.mDestroySurface.remove(this);
+            mWmService.mDestroySurface.remove(this);
         }
         if (oldVisibility == View.GONE) {
             mWinAnimator.mEnterAnimationPending = true;
@@ -4333,10 +4367,10 @@
         anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
                 displayInfo.appWidth, displayInfo.appHeight);
         anim.restrictDuration(MAX_ANIMATION_DURATION);
-        anim.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
+        anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */),
-                mService.mSurfaceAnimationRunner);
+                mWmService.mSurfaceAnimationRunner);
         startAnimation(mPendingTransaction, adapter);
         commitPendingTransaction();
     }
@@ -4350,7 +4384,7 @@
         transformFrameToSurfacePosition(left, top, newPosition);
         final AnimationAdapter adapter = new LocalAnimationAdapter(
                 new MoveAnimationSpec(oldPosition.x, oldPosition.y, newPosition.x, newPosition.y),
-                mService.mSurfaceAnimationRunner);
+                mWmService.mSurfaceAnimationRunner);
         startAnimation(getPendingTransaction(), adapter);
     }
 
@@ -4444,7 +4478,7 @@
         public boolean isFocused() {
             final WindowState outer = mOuter.get();
             if (outer != null) {
-                synchronized (outer.mService.mGlobalLock) {
+                synchronized (outer.mWmService.mGlobalLock) {
                     return outer.isFocused();
                 }
             }
@@ -4575,7 +4609,7 @@
             outPoint.offset(-parent.mWindowFrames.mFrame.left + parent.mAttrs.surfaceInsets.left,
                     -parent.mWindowFrames.mFrame.top + parent.mAttrs.surfaceInsets.top);
         } else if (parentWindowContainer != null) {
-            final Rect parentBounds = parentWindowContainer.getBounds();
+            final Rect parentBounds = parentWindowContainer.getDisplayedBounds();
             outPoint.offset(-parentBounds.left, -parentBounds.top);
         }
 
@@ -4765,6 +4799,14 @@
         mWindowFrames.setContentChanged(false);
     }
 
+    void setInsetProvider(InsetsSourceProvider insetProvider) {
+        mInsetProvider = insetProvider;
+    }
+
+    InsetsSourceProvider getInsetProvider() {
+        return mInsetProvider;
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
@@ -4776,7 +4818,7 @@
             final Animation anim = AnimationUtils.loadAnimation(mContext,
                     com.android.internal.R.anim.window_move_from_decor);
             mDuration = (long)
-                    (anim.computeDurationHint() * mService.getWindowAnimationScaleLocked());
+                    (anim.computeDurationHint() * mWmService.getWindowAnimationScaleLocked());
             mInterpolator = anim.getInterpolator();
             mFrom.set(fromX, fromY);
             mTo.set(toX, toY);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e090cc5..fb5c556 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -234,7 +234,7 @@
     private final Point mTmpPos = new Point();
 
     WindowStateAnimator(final WindowState win) {
-        final WindowManagerService service = win.mService;
+        final WindowManagerService service = win.mWmService;
 
         mService = service;
         mAnimator = service.mAnimator;
@@ -861,7 +861,7 @@
         // to find the surface size changed underneath it.
         final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
         if (relayout) {
-            mSurfaceResized = mSurfaceController.setSizeInTransaction(
+            mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
                     mTmpSize.width(), mTmpSize.height(), recoveringMemory);
         } else {
             mSurfaceResized = false;
@@ -1020,7 +1020,7 @@
                         mTmpPos.x = 0;
                         mTmpPos.y = 0;
                         if (stack != null) {
-                            stack.getRelativePosition(mTmpPos);
+                            stack.getRelativeDisplayedPosition(mTmpPos);
                         }
 
                         xOffset = -mTmpPos.x;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 6821e94..ce627e2 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -19,34 +19,28 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Surface.SCALING_MODE_SCALE_TO_WINDOW;
 
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
-import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.IBinder;
 import android.os.Debug;
+import android.os.IBinder;
 import android.os.Trace;
+import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowContentFrameStats;
-import android.view.Surface.OutOfResourcesException;
 
-import android.util.Slog;
-
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 
 class WindowSurfaceController {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowSurfaceController" : TAG_WM;
@@ -106,7 +100,7 @@
         final SurfaceControl.Builder b = win.makeSurface()
                 .setParent(win.getSurfaceControl())
                 .setName(name)
-                .setSize(w, h)
+                .setBufferSize(w, h)
                 .setFormat(format)
                 .setFlags(flags)
                 .setMetadata(windowType, ownerUid);
@@ -303,7 +297,7 @@
         }
     }
 
-    boolean setSizeInTransaction(int width, int height, boolean recoveringMemory) {
+    boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
         final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
         if (surfaceResized) {
             mSurfaceW = width;
@@ -312,7 +306,7 @@
             try {
                 if (SHOW_TRANSACTIONS) logSurface(
                         "SIZE " + width + "x" + height, null);
-                mSurfaceControl.setSize(width, height);
+                mSurfaceControl.setBufferSize(width, height);
             } catch (RuntimeException e) {
                 // If something goes wrong with the surface (such
                 // as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7193dd7..2ee58fe 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -113,7 +113,9 @@
             return;
         }
 
-        if (mService.mWaitingForConfig) {
+        // TODO(multi-display):
+        final DisplayContent defaultDisplay = mService.getDefaultDisplayContentLocked();
+        if (defaultDisplay.mWaitingForConfig) {
             // Our configuration has changed (most likely rotation), but we
             // don't yet have the complete configuration to report to
             // applications.  Don't do any window layout until we have it.
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 0cf79b6..9c13782 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -172,8 +172,8 @@
         setHidden(true);
 
         if (changed) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-            mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
+            mWmService.mWindowPlacerLocked.performSurfacePlacement();
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /*updateInputWindows*/);
         }
 
         if (delayed) {
@@ -202,7 +202,7 @@
         if (!mChildren.contains(win)) {
             if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
             addChild(win, mWindowComparator);
-            mService.mWindowsChanged = true;
+            mWmService.mWindowsChanged = true;
             // TODO: Should we also be setting layout needed here and other places?
         }
     }
@@ -265,7 +265,6 @@
         // to another display before the window behind
         // it is ready.
 
-        updateSurfaceSize(dc);
         super.onDisplayChanged(dc);
     }
 
@@ -327,9 +326,9 @@
      * system bars, or in other words extend outside of the "Decor Frame"
      */
     boolean canLayerAboveSystemBars() {
-        int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType,
+        int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
                 mOwnerCanManageAppTokens);
-        int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
+        int navLayer = mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
                 mOwnerCanManageAppTokens);
         return mOwnerCanManageAppTokens && (layer > navLayer);
     }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index bf83ac13..b85489a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -2,8 +2,6 @@
     name: "libservices.core",
     defaults: ["libservices.core-libs"],
 
-    cpp_std: "c++17",
-
     cflags: [
         "-Wall",
         "-Werror",
@@ -108,6 +106,7 @@
         "android.hardware.contexthub@1.0",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
+        "android.hardware.gnss@2.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
         "android.hardware.power@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ee8a08b..43d2dcf 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <cinttypes>
 #include <limits.h>
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
@@ -71,6 +72,7 @@
 
 #define INDENT "  "
 
+using android::base::ParseUint;
 using android::base::StringPrintf;
 
 namespace android {
@@ -81,11 +83,13 @@
 static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
 
 static struct {
+    jclass clazz;
     jmethodID notifyConfigurationChanged;
     jmethodID notifyInputDevicesChanged;
     jmethodID notifySwitch;
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
+    jmethodID notifyFocusChanged;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -94,6 +98,7 @@
     jmethodID checkInjectEventsPermission;
     jmethodID getVirtualKeyQuietTimeMillis;
     jmethodID getExcludedDeviceNames;
+    jmethodID getInputPortAssociations;
     jmethodID getKeyRepeatTimeout;
     jmethodID getKeyRepeatDelay;
     jmethodID getHoverTapTimeout;
@@ -147,15 +152,6 @@
     return value ? "true" : "false";
 }
 
-static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
-        const sp<InputApplicationHandle>& inputApplicationHandle) {
-    if (inputApplicationHandle == nullptr) {
-        return nullptr;
-    }
-    return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
-            getInputApplicationHandleObjLocalRef(env);
-}
-
 static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextObj, int32_t style,
         PointerIcon* outPointerIcon, SpriteIcon* outSpriteIcon) {
     status_t status = android_view_PointerIcon_loadSystemIcon(env,
@@ -191,6 +187,13 @@
     WM_ACTION_PASS_TO_USER = 1,
 };
 
+static std::string getStringElementFromJavaArray(JNIEnv* env, jobjectArray array, jsize index) {
+    jstring item = jstring(env->GetObjectArrayElement(array, index));
+    ScopedUtfChars chars(env, item);
+    std::string result(chars.c_str());
+    return result;
+}
+
 
 // --- NativeInputManager ---
 
@@ -249,6 +252,7 @@
             const sp<IBinder>& token,
             const std::string& reason);
     virtual void notifyInputChannelBroken(const sp<IBinder>& token);
+    virtual void notifyFocusChanged(const sp<IBinder>& token);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
     virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags);
@@ -342,6 +346,8 @@
     mInteractive = true;
 
     mInputManager = new InputManager(this, this);
+    defaultServiceManager()->addService(String16("inputflinger"),
+            mInputManager, false);
 }
 
 NativeInputManager::~NativeInputManager() {
@@ -457,20 +463,44 @@
     }
 
     outConfig->excludedDeviceNames.clear();
-    jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mServiceObj,
-            gServiceClassInfo.getExcludedDeviceNames));
+    jobjectArray excludedDeviceNames = jobjectArray(env->CallStaticObjectMethod(
+            gServiceClassInfo.clazz, gServiceClassInfo.getExcludedDeviceNames));
     if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) {
         jsize length = env->GetArrayLength(excludedDeviceNames);
         for (jsize i = 0; i < length; i++) {
-            jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
-            const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
-            outConfig->excludedDeviceNames.push_back(deviceNameChars);
-            env->ReleaseStringUTFChars(item, deviceNameChars);
-            env->DeleteLocalRef(item);
+            std::string deviceName = getStringElementFromJavaArray(env, excludedDeviceNames, i);
+            outConfig->excludedDeviceNames.push_back(deviceName);
         }
         env->DeleteLocalRef(excludedDeviceNames);
     }
 
+    // Associations between input ports and display ports
+    // The java method packs the information in the following manner:
+    // Original data: [{'inputPort1': '1'}, {'inputPort2': '2'}]
+    // Received data: ['inputPort1', '1', 'inputPort2', '2']
+    // So we unpack accordingly here.
+    outConfig->portAssociations.clear();
+    jobjectArray portAssociations = jobjectArray(env->CallStaticObjectMethod(
+            gServiceClassInfo.clazz, gServiceClassInfo.getInputPortAssociations));
+    if (!checkAndClearExceptionFromCallback(env, "getInputPortAssociations") && portAssociations) {
+        jsize length = env->GetArrayLength(portAssociations);
+        for (jsize i = 0; i < length / 2; i++) {
+            std::string inputPort = getStringElementFromJavaArray(env, portAssociations, 2 * i);
+            std::string displayPortStr =
+                    getStringElementFromJavaArray(env, portAssociations, 2 * i + 1);
+            uint8_t displayPort;
+            // Should already have been validated earlier, but do it here for safety.
+            bool success = ParseUint(displayPortStr, &displayPort);
+            if (!success) {
+                ALOGE("Could not parse entry in port configuration file, received: %s",
+                    displayPortStr.c_str());
+                continue;
+            }
+            outConfig->portAssociations.insert({inputPort, displayPort});
+        }
+        env->DeleteLocalRef(portAssociations);
+    }
+
     jint hoverTapTimeout = env->CallIntMethod(mServiceObj,
             gServiceClassInfo.getHoverTapTimeout);
     if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
@@ -656,13 +686,11 @@
 
     JNIEnv* env = jniEnv();
 
-    jobject inputApplicationHandleObj =
-            getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
     jobject tokenObj = javaObjectForIBinder(env, token);
     jstring reasonObj = env->NewStringUTF(reason.c_str());
 
     jlong newTimeout = env->CallLongMethod(mServiceObj,
-                gServiceClassInfo.notifyANR, inputApplicationHandleObj, tokenObj,
+                gServiceClassInfo.notifyANR, tokenObj,
                 reasonObj);
     if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
         newTimeout = 0; // abort dispatch
@@ -671,7 +699,6 @@
     }
 
     env->DeleteLocalRef(reasonObj);
-    env->DeleteLocalRef(inputApplicationHandleObj);
     return newTimeout;
 }
 
@@ -691,6 +718,22 @@
     }
 }
 
+void NativeInputManager::notifyFocusChanged(const sp<IBinder>& token) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyFocusChanged");
+#endif
+    ATRACE_CALL();
+
+    JNIEnv* env = jniEnv();
+
+    jobject tokenObj = javaObjectForIBinder(env, token);
+    if (tokenObj) {
+        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
+                tokenObj);
+        checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
+    }
+}
+
 void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
     ATRACE_CALL();
     JNIEnv* env = jniEnv();
@@ -1689,6 +1732,10 @@
         var = env->GetMethodID(clazz, methodName, methodDescriptor); \
         LOG_FATAL_IF(! (var), "Unable to find method " methodName);
 
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! (var), "Unable to find static method " methodName);
+
 #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
@@ -1703,6 +1750,7 @@
 
     jclass clazz;
     FIND_CLASS(clazz, "com/android/server/input/InputManagerService");
+    gServiceClassInfo.clazz = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
 
     GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz,
             "notifyConfigurationChanged", "(J)V");
@@ -1715,10 +1763,13 @@
 
     GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz,
             "notifyInputChannelBroken", "(Landroid/os/IBinder;)V");
+    
+    GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
+            "notifyFocusChanged", "(Landroid/os/IBinder;)V");
 
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
-            "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
+            "(Landroid/os/IBinder;Ljava/lang/String;)J");
 
     GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz,
             "filterInputEvent", "(Landroid/view/InputEvent;I)Z");
@@ -1743,9 +1794,12 @@
     GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz,
             "getVirtualKeyQuietTimeMillis", "()I");
 
-    GET_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
+    GET_STATIC_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
+    GET_STATIC_METHOD_ID(gServiceClassInfo.getInputPortAssociations, clazz,
+            "getInputPortAssociations", "()[Ljava/lang/String;");
+
     GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz,
             "getKeyRepeatTimeout", "()I");
 
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 9216005..4d0556c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -20,9 +20,11 @@
 
 #include <android/hardware/gnss/1.0/IGnss.h>
 #include <android/hardware/gnss/1.1/IGnss.h>
+#include <android/hardware/gnss/2.0/IGnss.h>
 
 #include <android/hardware/gnss/1.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.0/IGnssMeasurement.h>
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
 #include "hardware_legacy/power.h"
@@ -110,13 +112,15 @@
 
 using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
 using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
+using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
 using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
 using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
+using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
 using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
 using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback;
-
+using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback;
 
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
@@ -135,6 +139,7 @@
 sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
 sp<IGnss_V1_0> gnssHal = nullptr;
 sp<IGnss_V1_1> gnssHal_V1_1 = nullptr;
+sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
 sp<IAGnssRil> agnssRilIface = nullptr;
 sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
@@ -146,6 +151,7 @@
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
+sp<IGnssMeasurement_V2_0> gnssMeasurementIface_V2_0 = nullptr;
 sp<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
@@ -744,7 +750,9 @@
  * GnssMeasurementCallback implements the callback methods required for the
  * GnssMeasurement interface.
  */
-struct GnssMeasurementCallback : public IGnssMeasurementCallback_V1_1 {
+struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_0 {
+    Return<void> gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data)
+            override;
     Return<void> gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override;
     Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
  private:
@@ -761,6 +769,11 @@
     void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
 };
 
+Return<void> GnssMeasurementCallback::gnssMeasurementCb_2_0(
+        const IGnssMeasurementCallback_V2_0::GnssData& data) {
+    // TODO(b/119571122): implement gnssMeasurementCb_2_0
+    return Void();
+}
 
 Return<void> GnssMeasurementCallback::gnssMeasurementCb(
         const IGnssMeasurementCallback_V1_1::GnssData& data) {
@@ -1126,13 +1139,22 @@
 }
 
 static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
-    gnssHal_V1_1 = IGnss_V1_1::getService();
-    if (gnssHal_V1_1 == nullptr) {
-        ALOGD("gnssHal 1.1 was null, trying 1.0");
-        gnssHal = IGnss_V1_0::getService();
-    } else {
-        gnssHal = gnssHal_V1_1;
+    gnssHal_V2_0 = IGnss_V2_0::getService();
+    if (gnssHal_V2_0 != nullptr) {
+        gnssHal = gnssHal_V2_0;
+        gnssHal_V1_1 = gnssHal_V2_0;
+        return;
     }
+
+    ALOGD("gnssHal 2.0 was null, trying 1.1");
+    gnssHal_V1_1 = IGnss_V1_1::getService();
+    if (gnssHal_V1_1 != nullptr) {
+        gnssHal = gnssHal_V1_1;
+        return;
+    }
+
+    ALOGD("gnssHal 1.1 was null, trying 1.0");
+    gnssHal = IGnss_V1_0::getService();
 }
 
 static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) {
@@ -1187,110 +1209,120 @@
         LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
     }
 
-    if (gnssHal != nullptr) {
-      gnssHalDeathRecipient = new GnssDeathRecipient();
-      hardware::Return<bool> linked = gnssHal->linkToDeath(
-          gnssHalDeathRecipient, /*cookie*/ 0);
-        if (!linked.isOk()) {
-            ALOGE("Transaction error in linking to GnssHAL death: %s",
-                    linked.description().c_str());
-        } else if (!linked) {
-            ALOGW("Unable to link to GnssHal death notifications");
-        } else {
-            ALOGD("Link to death notification successful");
-        }
+    if (gnssHal == nullptr) {
+        ALOGE("Unable to get GPS service\n");
+        return;
+    }
 
-        auto gnssXtra = gnssHal->getExtensionXtra();
-        if (!gnssXtra.isOk()) {
-            ALOGD("Unable to get a handle to Xtra");
-        } else {
-            gnssXtraIface = gnssXtra;
-        }
+    gnssHalDeathRecipient = new GnssDeathRecipient();
+    hardware::Return<bool> linked = gnssHal->linkToDeath(gnssHalDeathRecipient, /*cookie*/ 0);
+    if (!linked.isOk()) {
+        ALOGE("Transaction error in linking to GnssHAL death: %s",
+                linked.description().c_str());
+    } else if (!linked) {
+        ALOGW("Unable to link to GnssHal death notifications");
+    } else {
+        ALOGD("Link to death notification successful");
+    }
 
-        auto gnssRil = gnssHal->getExtensionAGnssRil();
-        if (!gnssRil.isOk()) {
-            ALOGD("Unable to get a handle to AGnssRil");
-        } else {
-            agnssRilIface = gnssRil;
-        }
+    auto gnssXtra = gnssHal->getExtensionXtra();
+    if (!gnssXtra.isOk()) {
+        ALOGD("Unable to get a handle to Xtra");
+    } else {
+        gnssXtraIface = gnssXtra;
+    }
 
-        auto gnssAgnss = gnssHal->getExtensionAGnss();
-        if (!gnssAgnss.isOk()) {
-            ALOGD("Unable to get a handle to AGnss");
-        } else {
-            agnssIface = gnssAgnss;
-        }
+    auto gnssRil = gnssHal->getExtensionAGnssRil();
+    if (!gnssRil.isOk()) {
+        ALOGD("Unable to get a handle to AGnssRil");
+    } else {
+        agnssRilIface = gnssRil;
+    }
 
-        auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
-        if (!gnssNavigationMessage.isOk()) {
-            ALOGD("Unable to get a handle to GnssNavigationMessage");
-        } else {
-            gnssNavigationMessageIface = gnssNavigationMessage;
-        }
+    auto gnssAgnss = gnssHal->getExtensionAGnss();
+    if (!gnssAgnss.isOk()) {
+        ALOGD("Unable to get a handle to AGnss");
+    } else {
+        agnssIface = gnssAgnss;
+    }
 
-        if (gnssHal_V1_1 != nullptr) {
-             auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
-             if (!gnssMeasurement.isOk()) {
-                 ALOGD("Unable to get a handle to GnssMeasurement");
-             } else {
-                 gnssMeasurementIface_V1_1 = gnssMeasurement;
-                 gnssMeasurementIface = gnssMeasurementIface_V1_1;
-             }
-        } else {
-             auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
-             if (!gnssMeasurement_V1_0.isOk()) {
-                 ALOGD("Unable to get a handle to GnssMeasurement");
-             } else {
-                 gnssMeasurementIface = gnssMeasurement_V1_0;
-             }
-        }
+    auto gnssNavigationMessage = gnssHal->getExtensionGnssNavigationMessage();
+    if (!gnssNavigationMessage.isOk()) {
+        ALOGD("Unable to get a handle to GnssNavigationMessage");
+    } else {
+        gnssNavigationMessageIface = gnssNavigationMessage;
+    }
 
-        auto gnssDebug = gnssHal->getExtensionGnssDebug();
-        if (!gnssDebug.isOk()) {
-            ALOGD("Unable to get a handle to GnssDebug");
+    if (gnssHal_V2_0 != nullptr) {
+        // TODO(b/119638366): getExtensionGnssMeasurement_1_1 from gnssHal_V2_0
+        auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0();
+        if (!gnssMeasurement.isOk()) {
+            ALOGD("Unable to get a handle to GnssMeasurement_V2_0");
         } else {
-            gnssDebugIface = gnssDebug;
+            gnssMeasurementIface_V2_0 = gnssMeasurement;
+            gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0;
+            gnssMeasurementIface = gnssMeasurementIface_V2_0;
         }
+    } else if (gnssHal_V1_1 != nullptr) {
+         auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1();
+         if (!gnssMeasurement.isOk()) {
+             ALOGD("Unable to get a handle to GnssMeasurement_V1_1");
+         } else {
+             gnssMeasurementIface_V1_1 = gnssMeasurement;
+             gnssMeasurementIface = gnssMeasurementIface_V1_1;
+         }
+    } else {
+         auto gnssMeasurement_V1_0 = gnssHal->getExtensionGnssMeasurement();
+         if (!gnssMeasurement_V1_0.isOk()) {
+             ALOGD("Unable to get a handle to GnssMeasurement");
+         } else {
+             gnssMeasurementIface = gnssMeasurement_V1_0;
+         }
+    }
 
-        auto gnssNi = gnssHal->getExtensionGnssNi();
-        if (!gnssNi.isOk()) {
-            ALOGD("Unable to get a handle to GnssNi");
-        } else {
-            gnssNiIface = gnssNi;
-        }
+    auto gnssDebug = gnssHal->getExtensionGnssDebug();
+    if (!gnssDebug.isOk()) {
+        ALOGD("Unable to get a handle to GnssDebug");
+    } else {
+        gnssDebugIface = gnssDebug;
+    }
 
-        if (gnssHal_V1_1 != nullptr) {
-            auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
-            if (!gnssConfiguration.isOk()) {
-                ALOGD("Unable to get a handle to GnssConfiguration");
-            } else {
-                gnssConfigurationIface_V1_1 = gnssConfiguration;
-                gnssConfigurationIface = gnssConfigurationIface_V1_1;
-            }
-        } else {
-            auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
-            if (!gnssConfiguration_V1_0.isOk()) {
-                ALOGD("Unable to get a handle to GnssConfiguration");
-            } else {
-                gnssConfigurationIface = gnssConfiguration_V1_0;
-            }
-        }
+    auto gnssNi = gnssHal->getExtensionGnssNi();
+    if (!gnssNi.isOk()) {
+        ALOGD("Unable to get a handle to GnssNi");
+    } else {
+        gnssNiIface = gnssNi;
+    }
 
-        auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
-        if (!gnssGeofencing.isOk()) {
-            ALOGD("Unable to get a handle to GnssGeofencing");
+    if (gnssHal_V1_1 != nullptr) {
+        auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+        if (!gnssConfiguration.isOk()) {
+            ALOGD("Unable to get a handle to GnssConfiguration");
         } else {
-            gnssGeofencingIface = gnssGeofencing;
-        }
-
-        auto gnssBatching = gnssHal->getExtensionGnssBatching();
-        if (!gnssBatching.isOk()) {
-            ALOGD("Unable to get a handle to gnssBatching");
-        } else {
-            gnssBatchingIface = gnssBatching;
+            gnssConfigurationIface_V1_1 = gnssConfiguration;
+            gnssConfigurationIface = gnssConfigurationIface_V1_1;
         }
     } else {
-      ALOGE("Unable to get GPS service\n");
+        auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
+        if (!gnssConfiguration_V1_0.isOk()) {
+            ALOGD("Unable to get a handle to GnssConfiguration");
+        } else {
+            gnssConfigurationIface = gnssConfiguration_V1_0;
+        }
+    }
+
+    auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
+    if (!gnssGeofencing.isOk()) {
+        ALOGD("Unable to get a handle to GnssGeofencing");
+    } else {
+        gnssGeofencingIface = gnssGeofencing;
+    }
+
+    auto gnssBatching = gnssHal->getExtensionGnssBatching();
+    if (!gnssBatching.isOk()) {
+        ALOGD("Unable to get a handle to gnssBatching");
+    } else {
+        gnssBatchingIface = gnssBatching;
     }
 }
 
@@ -1820,10 +1852,11 @@
 
     sp<GnssMeasurementCallback> cbIface = new GnssMeasurementCallback();
     IGnssMeasurement_V1_0::GnssMeasurementStatus result =
-                    IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;;
-    if (gnssMeasurementIface_V1_1 != nullptr) {
-         result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface,
-                        enableFullTracking);
+            IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC;
+    if (gnssMeasurementIface_V2_0 != nullptr) {
+        result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking);
+    } else if (gnssMeasurementIface_V1_1 != nullptr) {
+        result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking);
     } else {
         if (enableFullTracking == JNI_TRUE) {
             // full tracking mode not supported in 1.0 HAL
@@ -1837,7 +1870,7 @@
               static_cast<int32_t>(result));
         return JNI_FALSE;
     } else {
-      ALOGD("gnss measurement infc has been enabled");
+        ALOGD("gnss measurement infc has been enabled");
     }
 
     return JNI_TRUE;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 6462d16..b9dabb9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -23,6 +23,9 @@
 
 import com.android.server.SystemService;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Defines the required interface for IDevicePolicyManager implemenation.
  *
@@ -97,4 +100,24 @@
     @Override
     public void installUpdateFromFile(ComponentName admin,
             ParcelFileDescriptor updateFileDescriptor, StartInstallingUpdateCallback listener) {}
+
+    @Override
+    public void addCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+    }
+
+    @Override
+    public boolean removeCrossProfileCalendarPackage(ComponentName admin, String packageName) {
+        return false;
+    }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackages(ComponentName admin) {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+            int userHandle) {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7751b4a..6fbb850 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -74,8 +74,10 @@
         .PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
         .STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
-import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+        .ADMIN_TYPE_DEVICE_OWNER;
+import static com.android.server.devicepolicy.TransferOwnershipMetadataManager
+        .ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -908,8 +910,12 @@
         private static final String TAG_IS_LOGOUT_ENABLED = "is_logout_enabled";
         private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
         private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
-        private static final String TAG_METERED_DATA_DISABLED_PACKAGES
-                = "metered_data_disabled_packages";
+        private static final String TAG_METERED_DATA_DISABLED_PACKAGES =
+                "metered_data_disabled_packages";
+        private static final String TAG_CROSS_PROFILE_CALENDAR_PACKAGES =
+                "cross-profile-calendar-packages";
+        private static final String TAG_PACKAGE = "package";
+
 
         DeviceAdminInfo info;
 
@@ -1030,6 +1036,9 @@
         String startUserSessionMessage = null;
         String endUserSessionMessage = null;
 
+        // The whitelist of packages that can access cross profile calendar APIs.
+        final Set<String> mCrossProfileCalendarPackages = new ArraySet<>();
+
         ActiveAdmin(DeviceAdminInfo _info, boolean parent) {
             info = _info;
             isParent = parent;
@@ -1299,6 +1308,12 @@
                 out.text(endUserSessionMessage);
                 out.endTag(null, TAG_END_USER_SESSION_MESSAGE);
             }
+            if (!mCrossProfileCalendarPackages.isEmpty()) {
+                out.startTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+                writeAttributeValuesToXml(
+                        out, TAG_PACKAGE, mCrossProfileCalendarPackages);
+                out.endTag(null, TAG_CROSS_PROFILE_CALENDAR_PACKAGES);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1491,6 +1506,9 @@
                     } else {
                         Log.w(LOG_TAG, "Missing text when loading end session message");
                     }
+                } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
+                    readAttributeValues(
+                            parser, TAG_PACKAGE, mCrossProfileCalendarPackages);
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1706,6 +1724,8 @@
                 pw.print(prefix);  pw.println("parentAdmin:");
                 parentAdmin.dump(prefix + "  ", pw);
             }
+            pw.print(prefix); pw.print("mCrossProfileCalendarPackages=");
+            pw.println(mCrossProfileCalendarPackages);
         }
     }
 
@@ -13339,9 +13359,77 @@
         }
     }
 
-
     private boolean isDeviceAB() {
         return "true".equalsIgnoreCase(android.os.SystemProperties
                 .get(AB_DEVICE_KEY, ""));
     }
+
+    @Override
+    public void addCrossProfileCalendarPackage(ComponentName who, String packageName) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (admin.mCrossProfileCalendarPackages.add(packageName)) {
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+    }
+
+    @Override
+    public boolean removeCrossProfileCalendarPackage(ComponentName who, String packageName) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        boolean isRemoved = false;
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            isRemoved = admin.mCrossProfileCalendarPackages.remove(packageName);
+            if (isRemoved) {
+                saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+            }
+        }
+        return isRemoved;
+    }
+
+    @Override
+    public List<String> getCrossProfileCalendarPackages(ComponentName who) {
+        if (!mHasFeature) {
+            return Collections.emptyList();
+        }
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return new ArrayList<String>(admin.mCrossProfileCalendarPackages);
+        }
+    }
+
+    @Override
+    public boolean isPackageAllowedToAccessCalendarForUser(String packageName,
+            int userHandle) {
+        if (!mHasFeature) {
+            return false;
+        }
+        Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+
+        enforceCrossUsersPermission(userHandle);
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
+            if (admin != null && admin.mCrossProfileCalendarPackages != null) {
+                return admin.mCrossProfileCalendarPackages.contains(packageName);
+            }
+        }
+        return false;
+    }
 }
diff --git a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
index 08fbf55..14912c4 100644
--- a/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/intelligence/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -19,9 +19,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.IBinder;
-import android.service.intelligence.IntelligenceService;
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.InteractionSessionId;
+import android.service.intelligence.SmartSuggestionsService;
 import android.service.intelligence.SnapshotData;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
@@ -59,7 +59,7 @@
         mService = service;
         mId = Preconditions.checkNotNull(sessionId);
         mRemoteService = new RemoteIntelligenceService(context,
-                IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+                SmartSuggestionsService.SERVICE_INTERFACE, serviceComponentName, userId, this,
                 bindInstantServiceAllowed, verbose);
         mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
     }
@@ -72,7 +72,7 @@
     }
 
     /**
-     * Notifies the {@link IntelligenceService} that the service started.
+     * Notifies the {@link SmartSuggestionsService} that the service started.
      */
     @GuardedBy("mLock")
     public void notifySessionStartedLocked() {
@@ -80,14 +80,14 @@
     }
 
     /**
-     * Notifies the {@link IntelligenceService} of a batch of events.
+     * Notifies the {@link SmartSuggestionsService} of a batch of events.
      */
     public void sendEventsLocked(@NonNull List<ContentCaptureEvent> events) {
         mRemoteService.onContentCaptureEventsRequest(mId, events);
     }
 
     /**
-     * Notifies the {@link IntelligenceService} of a snapshot of an activity.
+     * Notifies the {@link SmartSuggestionsService} of a snapshot of an activity.
      */
     @GuardedBy("mLock")
     public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) {
@@ -110,7 +110,7 @@
      * Cleans up the session and removes it from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
-     * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+     * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
      * request.
      */
     @GuardedBy("mLock")
@@ -126,7 +126,7 @@
      * Cleans up the session, but not removes it from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
-     * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+     * SmartSuggestionsService#onDestroyInteractionSession(InteractionSessionId)}
      * request.
      */
     @GuardedBy("mLock")
@@ -146,7 +146,7 @@
     }
 
     @Override // from RemoteScreenObservationServiceCallbacks
-    public void onServiceDied(AbstractRemoteService service) {
+    public void onServiceDied(AbstractRemoteService<?> service) {
         // TODO(b/111276913): implement (remove session from PerUserSession?)
         if (mService.isDebug()) {
             Slog.d(TAG, "onServiceDied() for " + mId);
@@ -176,6 +176,10 @@
         pw.println(mAutofillCallback != null);
     }
 
+    String toShortString() {
+        return mId.getValue() + ":" + mActivityToken;
+    }
+
     @Override
     public String toString() {
         return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
index 38810dd..4c68064 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -16,17 +16,24 @@
 
 package com.android.server.intelligence;
 
-import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
+import static android.Manifest.permission.MANAGE_SMART_SUGGESTIONS;
+import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.intelligence.InteractionSessionId;
+import android.util.Slog;
 import android.view.autofill.AutofillId;
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
@@ -41,6 +48,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -49,11 +57,14 @@
  * <p>The data collected by this service can be analyzed and combined with other sources to provide
  * contextual data in other areas of the system such as Autofill.
  */
+//TODO(b/111276913): rename once the final name is defined
 public final class IntelligenceManagerService extends
         AbstractMasterSystemService<IntelligenceManagerService, IntelligencePerUserService> {
 
     private static final String TAG = "IntelligenceManagerService";
 
+    static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
+
     @GuardedBy("mLock")
     private ActivityManagerInternal mAm;
 
@@ -66,7 +77,7 @@
     @Override // from AbstractMasterSystemService
     protected String getServiceSettingsProperty() {
         // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
-        return "intel_service";
+        return "smart_suggestions_service";
     }
 
     @Override // from AbstractMasterSystemService
@@ -77,7 +88,7 @@
 
     @Override // from SystemService
     public void onStart() {
-        publishBinderService(INTELLIGENCE_MANAGER_SERVICE,
+        publishBinderService(CONTENT_CAPTURE_MANAGER_SERVICE,
                 new IntelligenceManagerServiceStub());
         publishLocalService(IntelligenceManagerInternal.class, mLocalService);
     }
@@ -88,6 +99,61 @@
         service.destroyLocked();
     }
 
+    @Override // from AbstractMasterSystemService
+    protected void enforceCallingPermissionForManagement() {
+        getContext().enforceCallingPermission(MANAGE_SMART_SUGGESTIONS, TAG);
+    }
+
+    // Called by Shell command.
+    void destroySessions(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+        Slog.i(TAG, "destroySessions() for userId " + userId);
+        enforceCallingPermissionForManagement();
+
+        synchronized (mLock) {
+            if (userId != UserHandle.USER_ALL) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.destroySessionsLocked();
+                }
+            } else {
+                visitServicesLocked((s) -> s.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);
+        enforceCallingPermissionForManagement();
+
+        final Bundle resultData = new Bundle();
+        final ArrayList<String> sessions = new ArrayList<>();
+
+        synchronized (mLock) {
+            if (userId != UserHandle.USER_ALL) {
+                final IntelligencePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.listSessionsLocked(sessions);
+                }
+            } else {
+                visitServicesLocked((s) -> s.listSessionsLocked(sessions));
+            }
+        }
+
+        resultData.putStringArrayList(RECEIVER_BUNDLE_EXTRA_SESSIONS, sessions);
+        try {
+            receiver.send(0, resultData);
+        } catch (RemoteException e) {
+            // Just ignore it...
+        }
+    }
+
     private ActivityManagerInternal getAmInternal() {
         synchronized (mLock) {
             if (mAm == null) {
@@ -117,7 +183,7 @@
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        sessionId, flags, result);
+                        sessionId, flags, mAllowInstantService, result);
             }
         }
 
@@ -134,12 +200,13 @@
         }
 
         @Override
-        public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId) {
+        public void finishSession(@UserIdInt int userId, @NonNull InteractionSessionId sessionId,
+                @Nullable List<ContentCaptureEvent> events) {
             Preconditions.checkNotNull(sessionId);
 
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
-                service.finishSessionLocked(sessionId);
+                service.finishSessionLocked(sessionId, events);
             }
         }
 
@@ -151,6 +218,14 @@
                 dumpLocked("", pw);
             }
         }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+                throws RemoteException {
+            new IntelligenceServiceShellCommand(IntelligenceManagerService.this).exec(
+                    this, in, out, err, args, callback, resultReceiver);
+        }
     }
 
     private final class LocalService extends IntelligenceManagerInternal {
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
index 051f0d6..dbf8601 100644
--- a/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -22,6 +22,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppGlobals;
 import android.app.assist.AssistContent;
@@ -40,7 +41,7 @@
 import android.view.autofill.AutofillId;
 import android.view.autofill.IAutoFillManagerClient;
 import android.view.intelligence.ContentCaptureEvent;
-import android.view.intelligence.IntelligenceManager;
+import android.view.intelligence.ContentCaptureManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
@@ -48,11 +49,13 @@
 import com.android.server.intelligence.IntelligenceManagerInternal.AugmentedAutofillCallback;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Per-user instance of {@link IntelligenceManagerService}.
  */
+//TODO(b/111276913): rename once the final name is defined
 final class IntelligencePerUserService
         extends AbstractPerUserSystemService<IntelligencePerUserService,
             IntelligenceManagerService> {
@@ -85,24 +88,31 @@
             Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
             return null;
         }
-        if (!Manifest.permission.BIND_INTELLIGENCE_SERVICE.equals(si.permission)) {
-            Slog.w(TAG, "IntelligenceService from '" + si.packageName
+        if (!Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE.equals(si.permission)) {
+            Slog.w(TAG, "SmartSuggestionsService from '" + si.packageName
                     + "' does not require permission "
-                    + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+                    + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
             throw new SecurityException("Service does not require permission "
-                    + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+                    + Manifest.permission.BIND_SMART_SUGGESTIONS_SERVICE);
         }
         return si;
     }
 
+    @Override // from PerUserSystemService
+    @GuardedBy("mLock")
+    protected boolean updateLocked(boolean disabled) {
+        destroyLocked();
+        return super.updateLocked(disabled);
+    }
+
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
             @NonNull ComponentName componentName, int taskId, int displayId,
-            @NonNull InteractionSessionId sessionId, int flags,
+            @NonNull InteractionSessionId sessionId, int flags, boolean bindInstantServiceAllowed,
             @NonNull IResultReceiver resultReceiver) {
         if (!isEnabledLocked()) {
-            sendToClient(resultReceiver, IntelligenceManager.STATE_DISABLED);
+            sendToClient(resultReceiver, ContentCaptureManager.STATE_DISABLED);
             return;
         }
         final ComponentName serviceComponentName = getServiceComponentName();
@@ -125,13 +135,10 @@
             // TODO(b/111276913): check if local ids match and decide what to do if they don't
             // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
             // if not, move notifySessionStartedLocked() into session constructor
-            sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+            sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
             return;
         }
 
-        // TODO(b/117779333): get from mMaster once it's moved to superclass
-        final boolean bindInstantServiceAllowed = false;
-
         session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
                 this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
                 bindInstantServiceAllowed, mMaster.verbose);
@@ -141,12 +148,13 @@
         }
         mSessions.put(sessionId, session);
         session.notifySessionStartedLocked();
-        sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+        sendToClient(resultReceiver, ContentCaptureManager.STATE_ACTIVE);
     }
 
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
-    public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+    public void finishSessionLocked(@NonNull InteractionSessionId sessionId,
+            @Nullable List<ContentCaptureEvent> events) {
         if (!isEnabledLocked()) {
             return;
         }
@@ -158,8 +166,18 @@
             }
             return;
         }
+        if (events != null && !events.isEmpty()) {
+            // TODO(b/111276913): for now we're sending the events and the onDestroy() in 2 separate
+            // calls because it's not clear yet whether we'll change the manager to send events
+            // to the service directly (i.e., without passing through system server). Once we
+            // decide, we might need to split IIntelligenceService.onSessionLifecycle() in 2
+            // methods, one for start and another for finish (and passing the events to finish),
+            // otherwise the service might receive the 2 calls out of order.
+            session.sendEventsLocked(events);
+        }
         if (mMaster.verbose) {
-            Slog.v(TAG, "finishSession(): " + session);
+            Slog.v(TAG, "finishSession(" + (events == null ? 0 : events.size()) + " events): "
+                    + session);
         }
         session.removeSelfLocked(true);
     }
@@ -180,7 +198,7 @@
             return;
         }
         if (mMaster.verbose) {
-            Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+            Slog.v(TAG, "sendEvents(): id=" + sessionId + ", events=" + events.size());
         }
         session.sendEventsLocked(events);
     }
@@ -233,6 +251,11 @@
     @GuardedBy("mLock")
     public void destroyLocked() {
         if (mMaster.debug) Slog.d(TAG, "destroyLocked()");
+        destroySessionsLocked();
+    }
+
+    @GuardedBy("mLock")
+    void destroySessionsLocked() {
         final int numSessions = mSessions.size();
         for (int i = 0; i < numSessions; i++) {
             final ContentCaptureSession session = mSessions.valueAt(i);
@@ -241,6 +264,15 @@
         mSessions.clear();
     }
 
+    @GuardedBy("mLock")
+    void listSessionsLocked(ArrayList<String> output) {
+        final int numSessions = mSessions.size();
+        for (int i = 0; i < numSessions; i++) {
+            final ContentCaptureSession session = mSessions.valueAt(i);
+            output.add(session.toShortString());
+        }
+    }
+
     public AugmentedAutofillCallback requestAutofill(@NonNull IAutoFillManagerClient client,
             @NonNull IBinder activityToken, int autofillSessionId, @NonNull AutofillId focusedId) {
         synchronized (mLock) {
diff --git a/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
new file mode 100644
index 0000000..b7c1f78
--- /dev/null
+++ b/services/intelligence/java/com/android/server/intelligence/IntelligenceServiceShellCommand.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2018 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.intelligence;
+
+import static com.android.server.intelligence.IntelligenceManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Shell Command implementation for {@link IntelligenceManagerService}.
+ */
+//TODO(b/111276913): rename once the final name is defined
+public final class IntelligenceServiceShellCommand extends ShellCommand {
+
+    private final IntelligenceManagerService mService;
+
+    public IntelligenceServiceShellCommand(@NonNull IntelligenceManagerService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "list":
+                return requestList(pw);
+            case "destroy":
+                return requestDestroy(pw);
+            case "get":
+                return requestGet(pw);
+            case "set":
+                return requestSet(pw);
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        try (PrintWriter pw = getOutPrintWriter();) {
+            // TODO(b/111276913): rename "intelligence" once SELinux rule changed
+            pw.println("Intelligence Service (intelligence) commands:");
+            pw.println("  help");
+            pw.println("    Prints this help text.");
+            pw.println("");
+            pw.println("  get bind-instant-service-allowed");
+            pw.println("    Gets whether binding to services provided by instant apps is allowed");
+            pw.println("");
+            pw.println("  set bind-instant-service-allowed [true | false]");
+            pw.println("    Sets whether binding to services provided by instant apps is allowed");
+            pw.println("");
+            pw.println("  list sessions [--user USER_ID]");
+            pw.println("    Lists all pending sessions.");
+            pw.println("");
+            pw.println("  destroy sessions [--user USER_ID]");
+            pw.println("    Destroys all pending sessions.");
+            pw.println("");
+        }
+    }
+
+    private int requestGet(PrintWriter pw) {
+        final String what = getNextArgRequired();
+        switch(what) {
+            case "bind-instant-service-allowed":
+                return getBindInstantService(pw);
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int requestSet(PrintWriter pw) {
+        final String what = getNextArgRequired();
+
+        switch(what) {
+            case "bind-instant-service-allowed":
+                return setBindInstantService(pw);
+            default:
+                pw.println("Invalid set: " + what);
+                return -1;
+        }
+    }
+
+    private int getBindInstantService(PrintWriter pw) {
+        if (mService.getAllowInstantService()) {
+            pw.println("true");
+        } else {
+            pw.println("false");
+        }
+        return 0;
+    }
+
+    private int setBindInstantService(PrintWriter pw) {
+        final String mode = getNextArgRequired();
+        switch (mode.toLowerCase()) {
+            case "true":
+                mService.setAllowInstantService(true);
+                return 0;
+            case "false":
+                mService.setAllowInstantService(false);
+                return 0;
+            default:
+                pw.println("Invalid mode: " + mode);
+                return -1;
+        }
+    }
+
+    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) {
+                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));
+    }
+
+    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();
+        return waitForLatch(pw, latch);
+    }
+
+    private int waitForLatch(PrintWriter pw, CountDownLatch latch) {
+        try {
+            final boolean received = latch.await(5, TimeUnit.SECONDS);
+            if (!received) {
+                pw.println("Timed out after 5 seconds");
+                return -1;
+            }
+        } catch (InterruptedException e) {
+            pw.println("System call interrupted");
+            Thread.currentThread().interrupt();
+            return -1;
+        }
+        return 0;
+    }
+
+    private int getUserIdFromArgsOrAllUsers() {
+        if ("--user".equals(getNextArg())) {
+            return UserHandle.parseUserArg(getNextArgRequired());
+        }
+        return UserHandle.USER_ALL;
+    }
+}
diff --git a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
index 00c5b6a..d9f4f20 100644
--- a/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/intelligence/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.RemoteException;
+import android.service.intelligence.ContentCaptureEventsRequest;
 import android.service.intelligence.IIntelligenceService;
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.InteractionSessionId;
@@ -35,11 +36,13 @@
 import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.internal.os.IResultReceiver;
-import com.android.server.AbstractRemoteService;
+import com.android.server.AbstractMultiplePendingRequestsRemoteService;
 
 import java.util.List;
 
-final class RemoteIntelligenceService extends AbstractRemoteService {
+//TODO(b/111276913): rename once the final name is defined
+final class RemoteIntelligenceService
+        extends AbstractMultiplePendingRequestsRemoteService<RemoteIntelligenceService> {
 
     private static final String TAG = "RemoteIntelligenceService";
 
@@ -54,7 +57,7 @@
             RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
             boolean verbose) {
         super(context, serviceInterface, componentName, userId, callbacks,
-                bindInstantServiceAllowed, verbose);
+                bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2);
         mCallbacks = callbacks;
     }
 
@@ -194,7 +197,8 @@
 
         @Override // from MyPendingRequest
         public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
-            remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
+            remoteService.mService.onContentCaptureEventsRequest(mSessionId,
+                    new ContentCaptureEventsRequest(mEvents));
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 56f7cff..dadaf2a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -55,6 +55,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.IStorageManager;
+import android.sysprop.VoldProperties;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Slog;
@@ -120,6 +121,7 @@
 import com.android.server.role.RoleManagerService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
+import com.android.server.signedconfig.SignedConfigService;
 import com.android.server.soundtrigger.SoundTriggerService;
 import com.android.server.stats.StatsCompanionService;
 import com.android.server.statusbar.StatusBarManagerService;
@@ -654,7 +656,7 @@
         traceEnd();
 
         // Only run "core" apps if we're encrypting the device.
-        String cryptState = SystemProperties.get("vold.decrypt");
+        String cryptState = VoldProperties.decrypt().orElse("");
         if (ENCRYPTING_STATE.equals(cryptState)) {
             Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
             mOnlyCore = true;
@@ -994,6 +996,11 @@
             traceBeginAndSlog("PinnerService");
             mSystemServiceManager.startService(PinnerService.class);
             traceEnd();
+
+            traceBeginAndSlog("SignedConfigService");
+            SignedConfigService.registerUpdateReceiver(mSystemContext);
+            traceEnd();
+
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -1503,6 +1510,14 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("RuntimeService");
+            try {
+                ServiceManager.addService("runtime", new RuntimeService(context));
+            } catch (Throwable e) {
+                reportWtf("starting RuntimeService", e);
+            }
+            traceEnd();
+
             // timezone.RulesManagerService will prevent a device starting up if the chain of trust
             // required for safe time zone updates might be broken. RuleManagerService cannot do
             // this check when mOnlyCore == true, so we don't enable the service in this case.
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index dc55179..c9b9f3e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -146,13 +146,14 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     disabledMessage = dpmi.getPrintingDisabledReasonForUser(callingUserId);
+
+                    if (disabledMessage != null) {
+                        Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
+                                Toast.LENGTH_LONG).show();
+                    }
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
-                if (disabledMessage != null) {
-                    Toast.makeText(mContext, Looper.getMainLooper(), disabledMessage,
-                            Toast.LENGTH_LONG).show();
-                }
                 try {
                     adapter.start();
                 } catch (RemoteException re) {
diff --git a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
index f7bb68c..ba4caf44 100644
--- a/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,49 +16,27 @@
 
 package com.android.server.backup;
 
-import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
 import static com.android.server.backup.testing.TransportData.backupTransport;
-import static com.android.server.backup.testing.TransportData.d2dTransport;
-import static com.android.server.backup.testing.TransportData.localTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
-import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
 
-import android.app.backup.BackupManager;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
 import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
-import android.os.Binder;
-import android.os.HandlerThread;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
 
 import com.android.server.backup.testing.BackupManagerServiceTestUtils;
 import com.android.server.backup.testing.TransportData;
-import com.android.server.backup.testing.TransportTestUtils.TransportMock;
-import com.android.server.backup.transport.TransportNotRegisteredException;
-import com.android.server.testing.shadows.ShadowAppBackupUtils;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
-import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,977 +44,482 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowContextWrapper;
-import org.robolectric.shadows.ShadowLooper;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.shadows.ShadowSettings;
 
 import java.io.File;
-import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 
-/** Tests for the system service {@link BackupManagerService} that performs backup/restore. */
+/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAppBackupUtils.class})
 @Presubmit
 public class BackupManagerServiceTest {
-    private static final String TAG = "BMSTest";
-    private static final String PACKAGE_1 = "some.package.1";
-    private static final String PACKAGE_2 = "some.package.2";
+    private static final String TEST_PACKAGE = "package";
+    private static final String TEST_TRANSPORT = "transport";
 
+    @Mock private UserBackupManagerService mUserBackupManagerService;
     @Mock private TransportManager mTransportManager;
-    private HandlerThread mBackupThread;
-    private ShadowLooper mShadowBackupLooper;
-    private File mBaseStateDir;
-    private File mDataDir;
-    private ShadowContextWrapper mShadowContext;
+    private BackupManagerService mBackupManagerService;
     private Context mContext;
-    private TransportData mTransport;
-    private String mTransportName;
-    private ShadowPackageManager mShadowPackageManager;
 
-    /**
-     * Initialize state that {@link BackupManagerService} operations interact with. This includes
-     * setting up the transport, starting the backup thread, and creating backup data directories.
-     */
+    /** Initialize {@link BackupManagerService}. */
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mTransport = backupTransport();
-        mTransportName = mTransport.transportName;
-
-        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
-        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
-        // don't interfere with the tests.
-        mBackupThread = startSilentBackupThread(TAG);
-        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
-
-        ContextWrapper context = RuntimeEnvironment.application;
-        mShadowPackageManager = shadowOf(context.getPackageManager());
-        mContext = context;
-        mShadowContext = shadowOf(context);
-
-        File cacheDir = mContext.getCacheDir();
-        // Corresponds to /data/backup
-        mBaseStateDir = new File(cacheDir, "base_state");
-        // Corresponds to /cache/backup_stage
-        mDataDir = new File(cacheDir, "data");
+        Application application = RuntimeEnvironment.application;
+        mContext = application;
+        mBackupManagerService =
+                new BackupManagerService(
+                        application,
+                        new Trampoline(application),
+                        BackupManagerServiceTestUtils.startBackupThread(null),
+                        new File(application.getCacheDir(), "base_state"),
+                        new File(application.getCacheDir(), "data"),
+                        mTransportManager);
+        mBackupManagerService.setUserBackupManagerService(mUserBackupManagerService);
     }
 
     /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        mBackupThread.quit();
-        ShadowAppBackupUtils.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
-     * specifically to prevent overloading the logs in production.
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}.
+     * This is specifically to prevent overloading the logs in production.
      */
     @Test
-    public void testMoreDebug_isFalse() {
+    public void testMoreDebug_isFalse() throws Exception {
         boolean moreDebug = BackupManagerService.MORE_DEBUG;
 
         assertThat(moreDebug).isFalse();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns the
-     * current destination string of inputted transport if the transport is registered.
-     */
+    // TODO(b/118520567): Change the following tests to use the per-user instance of
+    // UserBackupManagerService once it's implemented. Currently these tests only test the straight
+    // forward redirection.
+
+    // ---------------------------------------------
+    // Backup agent tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenReturn("destinationString");
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testDataChanged_callsDataChangedForUser() throws Exception {
+        mBackupManagerService.dataChanged(TEST_PACKAGE);
 
-        String destination = backupManagerService.getDestinationString(mTransportName);
-
-        assertThat(destination).isEqualTo("destinationString");
+        verify(mUserBackupManagerService).dataChanged(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} returns {@code
-     * null} if the inputted transport is not registered.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testAgentConnected_callsAgentConnectedForUser() throws Exception {
+        IBinder agentBinder = mock(IBinder.class);
 
-        String destination = backupManagerService.getDestinationString(mTransportName);
+        mBackupManagerService.agentConnected(TEST_PACKAGE, agentBinder);
 
-        assertThat(destination).isNull();
+        verify(mUserBackupManagerService).agentConnected(TEST_PACKAGE, agentBinder);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#getDestinationString(String)} throws a {@link
-     * SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testDestinationString_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testAgentDisconnected_callsAgentDisconnectedForUser() throws Exception {
+        mBackupManagerService.agentDisconnected(TEST_PACKAGE);
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.getDestinationString(mTransportName));
+        verify(mUserBackupManagerService).agentDisconnected(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
-     * {@code false} when the given app is not eligible for backup.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testOpComplete_callsOpCompleteForUser() throws Exception {
+        mBackupManagerService.opComplete(/* token */ 0, /* result */ 0L);
 
-        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
-
-        assertThat(result).isFalse();
+        verify(mUserBackupManagerService).opComplete(/* token */ 0, /* result */ 0L);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} returns
-     * {@code true} when the given app is eligible for backup.
-     */
+    // ---------------------------------------------
+    // Transport tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testInitializeTransports_callsInitializeTransportsForUser() throws Exception {
+        String[] transports = {TEST_TRANSPORT};
 
-        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+        mBackupManagerService.initializeTransports(transports, /* observer */ null);
 
-        assertThat(result).isTrue();
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        verify(mUserBackupManagerService).initializeTransports(transports, /* observer */ null);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#isAppEligibleForBackup(String)} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testClearBackupData_callsClearBackupDataForUser() throws Exception {
+        mBackupManagerService.clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+        verify(mUserBackupManagerService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
-     * returns an {@code array} of only apps that are eligible for backup from an {@array} of
-     * inputted apps.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
-        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetCurrentTransport_callsGetCurrentTransportForUser() throws Exception {
+        mBackupManagerService.getCurrentTransport();
 
-        String[] filtered =
-                backupManagerService.filterAppsEligibleForBackup(
-                        new String[] {PACKAGE_1, PACKAGE_2});
-
-        assertThat(filtered).asList().containsExactly(PACKAGE_1);
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+        verify(mUserBackupManagerService).getCurrentTransport();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])}
-     * returns an empty {@code array} if no inputted apps are eligible for backup.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetCurrentTransportComponent_callsGetCurrentTransportComponentForUser()
+            throws Exception {
+        mBackupManagerService.getCurrentTransportComponent();
 
-        String[] filtered =
-                backupManagerService.filterAppsEligibleForBackup(
-                        new String[] {PACKAGE_1, PACKAGE_2});
-
-        assertThat(filtered).isEmpty();
+        verify(mUserBackupManagerService).getCurrentTransportComponent();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#filterAppsEligibleForBackup(String[])} throws
-     * a {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testListAllTransports_callsListAllTransportsForUser() throws Exception {
+        mBackupManagerService.listAllTransports();
 
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.filterAppsEligibleForBackup(
-                                new String[] {PACKAGE_1, PACKAGE_2}));
+        verify(mUserBackupManagerService).listAllTransports();
     }
 
-    /* Tests for select transport */
-
-    private ComponentName mNewTransportComponent;
-    private TransportData mNewTransport;
-    private TransportMock mNewTransportMock;
-    private TransportData mOldTransport;
-    private TransportMock mOldTransportMock;
-
-    private void setUpForSelectTransport() throws Exception {
-        mNewTransport = backupTransport();
-        mNewTransportComponent = mNewTransport.getTransportComponent();
-        mOldTransport = d2dTransport();
-        List<TransportMock> transportMocks =
-                setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
-        mNewTransportMock = transportMocks.get(0);
-        mOldTransportMock = transportMocks.get(1);
-        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
-                .thenReturn(mOldTransport.transportName);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} successfully
-     * switches the current transport to the inputted transport, returns the name of the old
-     * transport, and disposes of the transport client after the operation.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransport() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testListAllTransportComponents_callsListAllTransportComponentsForUser()
+            throws Exception {
+        mBackupManagerService.listAllTransportComponents();
 
-        String oldTransport =
-                backupManagerService.selectBackupTransport(mNewTransport.transportName);
-
-        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
-        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+        verify(mUserBackupManagerService).listAllTransportComponents();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransport(String)} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransport_withoutPermission() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testGetTransportWhitelist_callsGetTransportWhitelistForUser() throws Exception {
+        mBackupManagerService.getTransportWhitelist();
 
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+        verify(mUserBackupManagerService).getTransportWhitelist();
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
-     * transport and disposes of the transport client after the operation.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testSelectBackupTransportAsync() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
-                .thenReturn(BackupManager.SUCCESS);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
-        verify(callback).onSuccess(eq(mNewTransport.transportName));
-        verify(mTransportManager)
-                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
-     * transport and notifies the inputted callback of failure when it fails to register the
-     * transport.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
-                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
-        verify(callback).onFailure(anyInt());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
-     * transport and notifies the inputted callback of failure when the transport gets unregistered.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
-        setUpTransports(mTransportManager, mTransport.unregistered());
-        ComponentName newTransportComponent = mTransport.getTransportComponent();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
-                .thenReturn(BackupManager.SUCCESS);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
-        verify(callback).onFailure(anyInt());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#selectBackupTransportAsync(ComponentName,
-     * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
-     * have backup permission.
-     */
-    @Test
-    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
-        setUpForSelectTransport();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        ComponentName newTransportComponent = mNewTransport.getTransportComponent();
-
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.selectBackupTransportAsync(
-                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
-    }
-
-    private String getSettingsTransport() {
-        return ShadowSettings.ShadowSecure.getString(
-                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns the
-     * {@link ComponentName} of the currently selected transport.
-     */
-    @Test
-    public void testGetCurrentTransportComponent() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent())
-                .thenReturn(mTransport.getTransportComponent());
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
-     * {@code null} if there is no currently selected transport.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isNull();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} returns
-     * {@code null} if the currently selected transport is not registered.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        when(mTransportManager.getCurrentTransportComponent())
-                .thenThrow(TransportNotRegisteredException.class);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
-
-        assertThat(transportComponent).isNull();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#getCurrentTransportComponent()} throws a
-     * {@link SecurityException} if the caller does not have backup permission.
-     */
-    @Test
-    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
-    }
-
-    /* Tests for updating transport attributes */
-
-    private static final int PACKAGE_UID = 10;
-    private ComponentName mTransportComponent;
-    private int mTransportUid;
-
-    private void setUpForUpdateTransportAttributes() throws Exception {
-        mTransportComponent = mTransport.getTransportComponent();
-        String transportPackage = mTransportComponent.getPackageName();
-
-        ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
-        shadowPackageManager.addPackage(transportPackage);
-        shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
-
-        mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} succeeds if the uid of the transport is same as the
-     * uid of the caller.
-     */
-    @Test
-    public void
-            testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
-                    throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+    public void testUpdateTransportAttributes_callsUpdateTransportAttributesForUser()
+            throws Exception {
+        TransportData transport = backupTransport();
         Intent configurationIntent = new Intent();
         Intent dataManagementIntent = new Intent();
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
 
-        backupManagerService.updateTransportAttributes(
-                mTransportUid,
-                mTransportComponent,
-                mTransportName,
+        mBackupManagerService.updateTransportAttributes(
+                transport.getTransportComponent(),
+                transport.transportName,
                 configurationIntent,
                 "currentDestinationString",
                 dataManagementIntent,
                 "dataManagementLabel");
 
-        verify(mTransportManager)
+        verify(mUserBackupManagerService)
                 .updateTransportAttributes(
-                        eq(mTransportComponent),
-                        eq(mTransportName),
-                        eq(configurationIntent),
-                        eq("currentDestinationString"),
-                        eq(dataManagementIntent),
-                        eq("dataManagementLabel"));
+                        transport.getTransportComponent(),
+                        transport.transportName,
+                        configurationIntent,
+                        "currentDestinationString",
+                        dataManagementIntent,
+                        "dataManagementLabel");
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the uid of the
-     * transport is not equal to the uid of the caller.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+    public void testSelectBackupTransport_callsSelectBackupTransportForUser() throws Exception {
+        mBackupManagerService.selectBackupTransport(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectTransportAsync_callsSelectTransportAsyncForUser() throws Exception {
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        mBackupManagerService.selectBackupTransportAsync(
+                transport.getTransportComponent(), callback);
+
+        verify(mUserBackupManagerService)
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetConfigurationIntent_callsGetConfigurationIntentForUser() throws Exception {
+        mBackupManagerService.getConfigurationIntent(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDestinationString_callsGetDestinationStringForUser() throws Exception {
+        mBackupManagerService.getDestinationString(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementIntent_callsGetDataManagementIntentForUser() throws Exception {
+        mBackupManagerService.getDataManagementIntent(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementLabel_callsGetDataManagementLabelForUser() throws Exception {
+        mBackupManagerService.getDataManagementLabel(TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    // ---------------------------------------------
+    // Settings tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void setBackupEnabled_callsSetBackupEnabledForUser() throws Exception {
+        mBackupManagerService.setBackupEnabled(true);
+
+        verify(mUserBackupManagerService).setBackupEnabled(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void setAutoRestore_callsSetAutoRestoreForUser() throws Exception {
+        mBackupManagerService.setAutoRestore(true);
+
+        verify(mUserBackupManagerService).setAutoRestore(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupProvisioned_callsSetBackupProvisionedForUser() throws Exception {
+        mBackupManagerService.setBackupProvisioned(true);
+
+        verify(mUserBackupManagerService).setBackupProvisioned(true);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsBackupEnabled_callsIsBackupEnabledForUser() throws Exception {
+        mBackupManagerService.isBackupEnabled();
+
+        verify(mUserBackupManagerService).isBackupEnabled();
+    }
+
+    // ---------------------------------------------
+    // Backup tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testIsAppEligibleForBackup_callsIsAppEligibleForBackupForUser() throws Exception {
+        mBackupManagerService.isAppEligibleForBackup(TEST_PACKAGE);
+
+        verify(mUserBackupManagerService).isAppEligibleForBackup(TEST_PACKAGE);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFilterAppsEligibleForBackup_callsFilterAppsEligibleForBackupForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        String[] packages = {TEST_PACKAGE};
 
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid + 1,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+        mBackupManagerService.filterAppsEligibleForBackup(packages);
+
+        verify(mUserBackupManagerService).filterAppsEligibleForBackup(packages);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} transport component.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+    public void testBackupNow_callsBackupNowForUser() throws Exception {
+        mBackupManagerService.backupNow();
+
+        verify(mUserBackupManagerService).backupNow();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRequestBackup_callsRequestBackupForUser() throws Exception {
+        String[] packages = {TEST_PACKAGE};
+        IBackupObserver observer = mock(IBackupObserver.class);
+        IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+        mBackupManagerService.requestBackup(packages, observer, monitor, /* flags */ 0);
+
+        verify(mUserBackupManagerService).requestBackup(packages, observer, monitor, /* flags */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testCancelBackups_callsCancelBackupsForUser() throws Exception {
+        mBackupManagerService.cancelBackups();
+
+        verify(mUserBackupManagerService).cancelBackups();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginFullBackup_callsBeginFullBackupForUser() throws Exception {
+        FullBackupJob job = new FullBackupJob();
+
+        mBackupManagerService.beginFullBackup(job);
+
+        verify(mUserBackupManagerService).beginFullBackup(job);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testEndFullBackup_callsEndFullBackupForUser() throws Exception {
+        mBackupManagerService.endFullBackup();
+
+        verify(mUserBackupManagerService).endFullBackup();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testFullTransportBackup_callsFullTransportBackupForUser() throws Exception {
+        String[] packages = {TEST_PACKAGE};
+
+        mBackupManagerService.fullTransportBackup(packages);
+
+        verify(mUserBackupManagerService).fullTransportBackup(packages);
+    }
+
+    // ---------------------------------------------
+    // Restore tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testRestoreAtInstall_callsRestoreAtInstallForUser() throws Exception {
+        mBackupManagerService.restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+
+        verify(mUserBackupManagerService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testBeginRestoreSession_callsBeginRestoreSessionForUser() throws Exception {
+        mBackupManagerService.beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+
+        verify(mUserBackupManagerService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetAvailableRestoreToken_callsGetAvailableRestoreTokenForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        mBackupManagerService.getAvailableRestoreToken(TEST_PACKAGE);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                null,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+        verify(mUserBackupManagerService).getAvailableRestoreToken(TEST_PACKAGE);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} transport name.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    // ---------------------------------------------
+    // Adb backup/restore tests
+    // ---------------------------------------------
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                null,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSetBackupPassword_callsSetBackupPasswordForUser() throws Exception {
+        mBackupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+        verify(mUserBackupManagerService).setBackupPassword("currentPassword", "newPassword");
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given a {@code
-     * null} destination string.
-     */
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+    public void testHasBackupPassword_callsHasBackupPasswordForUser() throws Exception {
+        mBackupManagerService.hasBackupPassword();
+
+        verify(mUserBackupManagerService).hasBackupPassword();
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbBackup_callsAdbBackupForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        ParcelFileDescriptor parcelFileDescriptor =
+                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+        String[] packages = {TEST_PACKAGE};
+
+        mBackupManagerService.adbBackup(
+                parcelFileDescriptor,
+                /* includeApks */ true,
+                /* includeObbs */ true,
+                /* includeShared */ true,
+                /* doWidgets */ true,
+                /* doAllApps */ true,
+                /* includeSystem */ true,
+                /* doCompress */ true,
+                /* doKeyValue */ true,
+                packages);
+
+        verify(mUserBackupManagerService)
+                .adbBackup(
+                        parcelFileDescriptor,
+                        /* includeApks */ true,
+                        /* includeObbs */ true,
+                        /* includeShared */ true,
+                        /* doWidgets */ true,
+                        /* doAllApps */ true,
+                        /* includeSystem */ true,
+                        /* doCompress */ true,
+                        /* doKeyValue */ true,
+                        packages);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAdbRestore_callsAdbRestoreForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        ParcelFileDescriptor parcelFileDescriptor =
+                ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+
+        mBackupManagerService.adbRestore(parcelFileDescriptor);
+
+        verify(mUserBackupManagerService).adbRestore(parcelFileDescriptor);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testAcknowledgeAdbBackupOrRestore_callsAcknowledgeAdbBackupOrRestoreForUser()
             throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+        IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                null,
-                                new Intent(),
-                                "dataManagementLabel"));
+        mBackupManagerService.acknowledgeAdbBackupOrRestore(
+                /* token */ 0, /* allow */ true, "currentPassword", "encryptionPassword", observer);
+
+        verify(mUserBackupManagerService)
+                .acknowledgeAdbBackupOrRestore(
+                        /* token */ 0,
+                        /* allow */ true,
+                        "currentPassword",
+                        "encryptionPassword",
+                        observer);
     }
 
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link RuntimeException} if given either a
-     * {@code null} data management label or {@code null} data management intent, but not both.
-     */
+    // ---------------------------------------------
+    //  Service tests
+    // ---------------------------------------------
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void
-            testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
-                    throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
+    public void testDump_callsDumpForUser() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        FileDescriptor fileDescriptor = new FileDescriptor();
+        PrintWriter printWriter = new PrintWriter(testFile);
+        String[] args = {"1", "2"};
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                null,
-                                "dataManagementLabel"));
+        mBackupManagerService.dump(fileDescriptor, printWriter, args);
 
-        expectThrows(
-                RuntimeException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                null));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} succeeds if the caller has backup permission.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
-            throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        Intent configurationIntent = new Intent();
-        Intent dataManagementIntent = new Intent();
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        backupManagerService.updateTransportAttributes(
-                mTransportUid,
-                mTransportComponent,
-                mTransportName,
-                configurationIntent,
-                "currentDestinationString",
-                dataManagementIntent,
-                "dataManagementLabel");
-
-        verify(mTransportManager)
-                .updateTransportAttributes(
-                        eq(mTransportComponent),
-                        eq(mTransportName),
-                        eq(configurationIntent),
-                        eq("currentDestinationString"),
-                        eq(dataManagementIntent),
-                        eq("dataManagementLabel"));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#updateTransportAttributes(int, ComponentName,
-     * String, Intent, String, Intent, String)} throws a {@link SecurityException} if the caller
-     * does not have backup permission.
-     */
-    @Test
-    public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
-            throws Exception {
-        setUpForUpdateTransportAttributes();
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.updateTransportAttributes(
-                                mTransportUid,
-                                mTransportComponent,
-                                mTransportName,
-                                new Intent(),
-                                "currentDestinationString",
-                                new Intent(),
-                                "dataManagementLabel"));
-    }
-
-    /* Tests for request backup */
-
-    @Mock private IBackupObserver mObserver;
-
-    private void setUpForRequestBackup(String... packages) throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        for (String packageName : packages) {
-            mShadowPackageManager.addPackage(packageName);
-            ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
-        }
-        setUpCurrentTransport(mTransportManager, mTransport);
-    }
-
-    private void tearDownForRequestBackup() {
-        ShadowKeyValueBackupTask.reset();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws a {@link SecurityException} if the caller does not have backup permission.
-     */
-    @Test
-    public void testRequestBackup_whenPermissionDenied() throws Exception {
-        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                SecurityException.class,
-                () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
-     */
-    @Test
-    public void testRequestBackup_whenPackagesNull() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                IllegalArgumentException.class,
-                () -> backupManagerService.requestBackup(null, mObserver, 0));
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
-     * packages.
-     */
-    @Test
-    public void testRequestBackup_whenPackagesEmpty() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-
-        expectThrows(
-                IllegalArgumentException.class,
-                () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
-     */
-    @Test
-    public void testRequestBackup_whenBackupDisabled() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(false);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
-     * through SUW.
-     */
-    @Test
-    public void testRequestBackup_whenNotProvisioned() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setProvisioned(false);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
-     * registered.
-     */
-    @Test
-    public void testRequestBackup_whenTransportNotRegistered() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        setUpCurrentTransport(mTransportManager, mTransport.unregistered());
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
-        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
-     * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
-     */
-    @Test
-    public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        mShadowPackageManager.addPackage(PACKAGE_1);
-        setUpCurrentTransport(mTransportManager, mTransport);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-        // Haven't set PACKAGE_1 as eligible
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
-     * package succeeds.
-     */
-    @Test
-    @Config(shadows = ShadowKeyValueBackupTask.class)
-    public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
-        setUpForRequestBackup(PACKAGE_1);
-        BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
-        assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
-        assertThat(shadowTask.getPendingFullBackups()).isEmpty();
-        // TODO: Assert more about KeyValueBackupTask
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#requestBackup(String[], IBackupObserver,
-     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
-     * backup package succeeds.
-     */
-    @Test
-    @Config(shadows = ShadowKeyValueBackupTask.class)
-    public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
-        setUpForRequestBackup(PACKAGE_1);
-        ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
-        BackupManagerService backupManagerService = createBackupManagerServiceForRequestBackup();
-
-        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
-
-        mShadowBackupLooper.runToEndOfTasks();
-        assertThat(result).isEqualTo(BackupManager.SUCCESS);
-        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
-        assertThat(shadowTask.getQueue()).isEmpty();
-        assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
-        // TODO: Assert more about KeyValueBackupTask
-        tearDownForRequestBackup();
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#backupNow()} clears the calling identity
-     * for scheduling a job and then restores the original calling identity after the operation.
-     */
-    @Test
-    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
-    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        setUpPowerManager(backupManagerService);
-        ShadowBinder.setCallingUid(1);
-
-        backupManagerService.backupNow();
-
-        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(Binder.getCallingUid()).isEqualTo(1);
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#backupNow()} restores the original calling
-     * identity if an exception is thrown during execution.
-     */
-    @Test
-    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
-    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        setUpPowerManager(backupManagerService);
-        ShadowBinder.setCallingUid(1);
-
-        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
-        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
-                .isEqualTo(ShadowBinder.LOCAL_UID);
-        assertThat(Binder.getCallingUid()).isEqualTo(1);
-    }
-
-    private BackupManagerService createBackupManagerServiceForRequestBackup() {
-        BackupManagerService backupManagerService = createInitializedBackupManagerService();
-        backupManagerService.setEnabled(true);
-        backupManagerService.setProvisioned(true);
-        return backupManagerService;
-    }
-
-    /**
-     * Test verifying that {@link BackupManagerService#BackupManagerService(Context, Trampoline,
-     * HandlerThread, File, File, TransportManager)} posts a transport registration task to the
-     * backup handler thread.
-     */
-    @Test
-    public void testConstructor_postRegisterTransports() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
-        createBackupManagerService();
-
-        mShadowBackupLooper.runToEndOfTasks();
-        verify(mTransportManager).registerTransports();
-    }
-
-    /**
-     * Test verifying that the {@link BackupManagerService#BackupManagerService(Context, Trampoline,
-     * HandlerThread, File, File, TransportManager)} does not directly register transports in its
-     * own thread.
-     */
-    @Test
-    public void testConstructor_doesNotRegisterTransportsSynchronously() {
-        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
-
-        createBackupManagerService();
-
-        // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
-        verify(mTransportManager, never()).registerTransports();
-    }
-
-    private BackupManagerService createBackupManagerService() {
-        return new BackupManagerService(
-                mContext,
-                new Trampoline(mContext),
-                mBackupThread,
-                mBaseStateDir,
-                mDataDir,
-                mTransportManager);
-    }
-
-    private BackupManagerService createInitializedBackupManagerService() {
-        return BackupManagerServiceTestUtils.createInitializedBackupManagerService(
-                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
-    }
-
-    private void setUpPowerManager(BackupManagerService backupManagerService) {
-        PowerManager powerManagerMock = mock(PowerManager.class);
-        when(powerManagerMock.getPowerSaveState(anyInt()))
-                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
-        backupManagerService.setPowerManager(powerManagerMock);
-    }
-
-    /**
-     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
-     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
-     */
-    @Implements(KeyValueBackupJob.class)
-    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
-        /**
-         * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
-         * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
-         */
-        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
-            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
-            throw new IllegalArgumentException();
-        }
+        verify(mUserBackupManagerService).dump(fileDescriptor, printWriter, args);
     }
 }
diff --git a/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
new file mode 100644
index 0000000..9d43819
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2018 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.backup;
+
+import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startSilentBackupThread;
+import static com.android.server.backup.testing.TransportData.backupTransport;
+import static com.android.server.backup.testing.TransportData.d2dTransport;
+import static com.android.server.backup.testing.TransportData.localTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpCurrentTransport;
+import static com.android.server.backup.testing.TransportTestUtils.setUpTransports;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.backup.testing.BackupManagerServiceTestUtils;
+import com.android.server.backup.testing.TransportData;
+import com.android.server.backup.testing.TransportTestUtils.TransportMock;
+import com.android.server.backup.transport.TransportNotRegisteredException;
+import com.android.server.testing.shadows.ShadowAppBackupUtils;
+import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowKeyValueBackupJob;
+import com.android.server.testing.shadows.ShadowKeyValueBackupTask;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowContextWrapper;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.shadows.ShadowSettings;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Tests for the per-user instance of the backup/restore system service {@link
+ * UserBackupManagerService} that performs operations for its target user.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowAppBackupUtils.class})
+@Presubmit
+public class UserBackupManagerServiceTest {
+    private static final String TAG = "BMSTest";
+    private static final String PACKAGE_1 = "some.package.1";
+    private static final String PACKAGE_2 = "some.package.2";
+
+    @Mock private TransportManager mTransportManager;
+    private HandlerThread mBackupThread;
+    private ShadowLooper mShadowBackupLooper;
+    private File mBaseStateDir;
+    private File mDataDir;
+    private ShadowContextWrapper mShadowContext;
+    private Context mContext;
+    private TransportData mTransport;
+    private String mTransportName;
+    private ShadowPackageManager mShadowPackageManager;
+
+    /**
+     * Initialize state that {@link UserBackupManagerService} operations interact with. This
+     * includes setting up the transport, starting the backup thread, and creating backup data
+     * directories.
+     */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTransport = backupTransport();
+        mTransportName = mTransport.transportName;
+
+        // Unrelated exceptions are thrown in the backup thread. Until we mock everything properly
+        // we should not fail tests because of this. This is not flakiness, the exceptions thrown
+        // don't interfere with the tests.
+        mBackupThread = startSilentBackupThread(TAG);
+        mShadowBackupLooper = shadowOf(mBackupThread.getLooper());
+
+        ContextWrapper context = RuntimeEnvironment.application;
+        mShadowPackageManager = shadowOf(context.getPackageManager());
+        mContext = context;
+        mShadowContext = shadowOf(context);
+
+        File cacheDir = mContext.getCacheDir();
+        // Corresponds to /data/backup
+        mBaseStateDir = new File(cacheDir, "base_state");
+        // Corresponds to /cache/backup_stage
+        mDataDir = new File(cacheDir, "data");
+    }
+
+    /**
+     * Clean up and reset state that was created for testing {@link UserBackupManagerService}
+     * operations.
+     */
+    @After
+    public void tearDown() throws Exception {
+        mBackupThread.quit();
+        ShadowAppBackupUtils.reset();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns the
+     * current destination string of inputted transport if the transport is registered.
+     */
+    @Test
+    public void testDestinationString() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenReturn("destinationString");
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String destination = backupManagerService.getDestinationString(mTransportName);
+
+        assertThat(destination).isEqualTo("destinationString");
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} returns
+     * {@code null} if the inputted transport is not registered.
+     */
+    @Test
+    public void testDestinationString_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String destination = backupManagerService.getDestinationString(mTransportName);
+
+        assertThat(destination).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getDestinationString(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testDestinationString_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getTransportCurrentDestinationString(eq(mTransportName)))
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.getDestinationString(mTransportName));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+     * {@code false} when the given app is not eligible for backup.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_whenAppNotEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+        assertThat(result).isFalse();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} returns
+     * {@code true} when the given app is eligible for backup.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_whenAppEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        TransportMock transportMock = setUpCurrentTransport(mTransportManager, backupTransport());
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        boolean result = backupManagerService.isAppEligibleForBackup(PACKAGE_1);
+
+        assertThat(result).isTrue();
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#isAppEligibleForBackup(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testIsAppEligibleForBackup_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.isAppEligibleForBackup(PACKAGE_1));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * returns an {@code array} of only apps that are eligible for backup from an {@array} of
+     * inputted apps.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        TransportMock transportMock = setUpCurrentTransport(mTransportManager, mTransport);
+        ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(PACKAGE_1);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String[] filtered =
+                backupManagerService.filterAppsEligibleForBackup(
+                        new String[] {PACKAGE_1, PACKAGE_2});
+
+        assertThat(filtered).asList().containsExactly(PACKAGE_1);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(transportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * returns an empty {@code array} if no inputted apps are eligible for backup.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup_whenNoneIsEligible() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String[] filtered =
+                backupManagerService.filterAppsEligibleForBackup(
+                        new String[] {PACKAGE_1, PACKAGE_2});
+
+        assertThat(filtered).isEmpty();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#filterAppsEligibleForBackup(String[])}
+     * throws a {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testFilterAppsEligibleForBackup_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.filterAppsEligibleForBackup(
+                                new String[] {PACKAGE_1, PACKAGE_2}));
+    }
+
+    /* Tests for select transport */
+
+    private ComponentName mNewTransportComponent;
+    private TransportData mNewTransport;
+    private TransportMock mNewTransportMock;
+    private TransportData mOldTransport;
+    private TransportMock mOldTransportMock;
+
+    private void setUpForSelectTransport() throws Exception {
+        mNewTransport = backupTransport();
+        mNewTransportComponent = mNewTransport.getTransportComponent();
+        mOldTransport = d2dTransport();
+        List<TransportMock> transportMocks =
+                setUpTransports(mTransportManager, mNewTransport, mOldTransport, localTransport());
+        mNewTransportMock = transportMocks.get(0);
+        mOldTransportMock = transportMocks.get(1);
+        when(mTransportManager.selectTransport(eq(mNewTransport.transportName)))
+                .thenReturn(mOldTransport.transportName);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)}
+     * successfully switches the current transport to the inputted transport, returns the name of
+     * the old transport, and disposes of the transport client after the operation.
+     */
+    @Test
+    public void testSelectBackupTransport() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        String oldTransport =
+                backupManagerService.selectBackupTransport(mNewTransport.transportName);
+
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        assertThat(oldTransport).isEqualTo(mOldTransport.transportName);
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransport(String)} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testSelectBackupTransport_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.selectBackupTransport(mNewTransport.transportName));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} successfully switches the current transport to the inputted
+     * transport and disposes of the transport client after the operation.
+     */
+    @Test
+    public void testSelectBackupTransportAsync() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isEqualTo(mNewTransport.transportName);
+        verify(callback).onSuccess(eq(mNewTransport.transportName));
+        verify(mTransportManager)
+                .disposeOfTransportClient(eq(mNewTransportMock.transportClient), any());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+     * transport and notifies the inputted callback of failure when it fails to register the
+     * transport.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_whenRegistrationFails() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(mNewTransportComponent)))
+                .thenReturn(BackupManager.ERROR_TRANSPORT_UNAVAILABLE);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(mNewTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(mNewTransport.transportName);
+        verify(callback).onFailure(anyInt());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} does not switch the current transport to the inputted
+     * transport and notifies the inputted callback of failure when the transport gets unregistered.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_whenTransportGetsUnregistered() throws Exception {
+        setUpTransports(mTransportManager, mTransport.unregistered());
+        ComponentName newTransportComponent = mTransport.getTransportComponent();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.registerAndSelectTransport(eq(newTransportComponent)))
+                .thenReturn(BackupManager.SUCCESS);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(newTransportComponent, callback);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(getSettingsTransport()).isNotEqualTo(mTransportName);
+        verify(callback).onFailure(anyInt());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#selectBackupTransportAsync(ComponentName,
+     * ISelectBackupTransportCallback)} throws a {@link SecurityException} if the caller does not
+     * have backup permission.
+     */
+    @Test
+    public void testSelectBackupTransportAsync_withoutPermission() throws Exception {
+        setUpForSelectTransport();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        ComponentName newTransportComponent = mNewTransport.getTransportComponent();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.selectBackupTransportAsync(
+                                newTransportComponent, mock(ISelectBackupTransportCallback.class)));
+    }
+
+    private String getSettingsTransport() {
+        return ShadowSettings.ShadowSecure.getString(
+                mContext.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * the {@link ComponentName} of the currently selected transport.
+     */
+    @Test
+    public void testGetCurrentTransportComponent() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenReturn(mTransport.getTransportComponent());
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isEqualTo(mTransport.getTransportComponent());
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * {@code null} if there is no currently selected transport.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_whenNoTransportSelected() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent()).thenReturn(null);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} returns
+     * {@code null} if the currently selected transport is not registered.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        when(mTransportManager.getCurrentTransportComponent())
+                .thenThrow(TransportNotRegisteredException.class);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        ComponentName transportComponent = backupManagerService.getCurrentTransportComponent();
+
+        assertThat(transportComponent).isNull();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#getCurrentTransportComponent()} throws a
+     * {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testGetCurrentTransportComponent_withoutPermission() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(SecurityException.class, backupManagerService::getCurrentTransportComponent);
+    }
+
+    /* Tests for updating transport attributes */
+
+    private static final int PACKAGE_UID = 10;
+    private ComponentName mTransportComponent;
+    private int mTransportUid;
+
+    private void setUpForUpdateTransportAttributes() throws Exception {
+        mTransportComponent = mTransport.getTransportComponent();
+        String transportPackage = mTransportComponent.getPackageName();
+
+        ShadowPackageManager shadowPackageManager = shadowOf(mContext.getPackageManager());
+        shadowPackageManager.addPackage(transportPackage);
+        shadowPackageManager.setPackagesForUid(PACKAGE_UID, transportPackage);
+
+        mTransportUid = mContext.getPackageManager().getPackageUid(transportPackage, 0);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} succeeds if the uid of the transport
+     * is same as the uid of the caller.
+     */
+    @Test
+    public void
+            testUpdateTransportAttributes_whenTransportUidEqualsCallingUid_callsTransportManager()
+                    throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        backupManagerService.updateTransportAttributes(
+                mTransportUid,
+                mTransportComponent,
+                mTransportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mTransportManager)
+                .updateTransportAttributes(
+                        eq(mTransportComponent),
+                        eq(mTransportName),
+                        eq(configurationIntent),
+                        eq("currentDestinationString"),
+                        eq(dataManagementIntent),
+                        eq("dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+     * the uid of the transport is not equal to the uid of the caller.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenTransportUidNotEqualToCallingUid_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid + 1,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} transport component.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenTransportComponentNull_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                null,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} transport name.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenNameNull_throwsException() throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                null,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given a {@code null} destination string.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenCurrentDestinationStringNull_throwsException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                null,
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link RuntimeException} if
+     * given either a {@code null} data management label or {@code null} data management intent, but
+     * not both.
+     */
+    @Test
+    public void
+            testUpdateTransportAttributes_whenDataManagementArgsNullityDontMatch_throwsException()
+                    throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                null,
+                                "dataManagementLabel"));
+
+        expectThrows(
+                RuntimeException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                null));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} succeeds if the caller has backup
+     * permission.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenPermissionGranted_callsThroughToTransportManager()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        Intent configurationIntent = new Intent();
+        Intent dataManagementIntent = new Intent();
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        backupManagerService.updateTransportAttributes(
+                mTransportUid,
+                mTransportComponent,
+                mTransportName,
+                configurationIntent,
+                "currentDestinationString",
+                dataManagementIntent,
+                "dataManagementLabel");
+
+        verify(mTransportManager)
+                .updateTransportAttributes(
+                        eq(mTransportComponent),
+                        eq(mTransportName),
+                        eq(configurationIntent),
+                        eq("currentDestinationString"),
+                        eq(dataManagementIntent),
+                        eq("dataManagementLabel"));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#updateTransportAttributes(int,
+     * ComponentName, String, Intent, String, Intent, String)} throws a {@link SecurityException} if
+     * the caller does not have backup permission.
+     */
+    @Test
+    public void testUpdateTransportAttributes_whenPermissionDenied_throwsSecurityException()
+            throws Exception {
+        setUpForUpdateTransportAttributes();
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.updateTransportAttributes(
+                                mTransportUid,
+                                mTransportComponent,
+                                mTransportName,
+                                new Intent(),
+                                "currentDestinationString",
+                                new Intent(),
+                                "dataManagementLabel"));
+    }
+
+    /* Tests for request backup */
+
+    @Mock private IBackupObserver mObserver;
+
+    private void setUpForRequestBackup(String... packages) throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        for (String packageName : packages) {
+            mShadowPackageManager.addPackage(packageName);
+            ShadowAppBackupUtils.setAppRunningAndEligibleForBackupWithTransport(packageName);
+        }
+        setUpCurrentTransport(mTransportManager, mTransport);
+    }
+
+    private void tearDownForRequestBackup() {
+        ShadowKeyValueBackupTask.reset();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws a {@link SecurityException} if the caller does not have backup permission.
+     */
+    @Test
+    public void testRequestBackup_whenPermissionDenied() throws Exception {
+        mShadowContext.denyPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                SecurityException.class,
+                () -> backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0));
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws an {@link IllegalArgumentException} if passed {@null} for packages.
+     */
+    @Test
+    public void testRequestBackup_whenPackagesNull() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> backupManagerService.requestBackup(null, mObserver, 0));
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} throws an {@link IllegalArgumentException} if passed an empty {@code array} for
+     * packages.
+     */
+    @Test
+    public void testRequestBackup_whenPackagesEmpty() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> backupManagerService.requestBackup(new String[0], mObserver, 0));
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if backup is disabled.
+     */
+    @Test
+    public void testRequestBackup_whenBackupDisabled() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(false);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the system user hasn't gone
+     * through SUW.
+     */
+    @Test
+    public void testRequestBackup_whenNotProvisioned() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setProvisioned(false);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#ERROR_TRANSPORT_ABORTED} if the current transport is not
+     * registered.
+     */
+    @Test
+    public void testRequestBackup_whenTransportNotRegistered() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        setUpCurrentTransport(mTransportManager, mTransport.unregistered());
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.ERROR_TRANSPORT_ABORTED);
+        verify(mObserver).backupFinished(BackupManager.ERROR_TRANSPORT_ABORTED);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and notifies the observer of {@link
+     * BackupManager#ERROR_BACKUP_NOT_ALLOWED} if the specified app is not eligible for backup.
+     */
+    @Test
+    public void testRequestBackup_whenAppNotEligibleForBackup() throws Exception {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        mShadowPackageManager.addPackage(PACKAGE_1);
+        setUpCurrentTransport(mTransportManager, mTransport);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+        // Haven't set PACKAGE_1 as eligible
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        verify(mObserver).onResult(PACKAGE_1, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+        // TODO: We probably don't need to kick-off KeyValueBackupTask when list is empty
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a key value
+     * package succeeds.
+     */
+    @Test
+    @Config(shadows = ShadowKeyValueBackupTask.class)
+    public void testRequestBackup_whenPackageIsKeyValue() throws Exception {
+        setUpForRequestBackup(PACKAGE_1);
+        UserBackupManagerService backupManagerService =
+                createBackupManagerServiceForRequestBackup();
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+        assertThat(shadowTask.getQueue()).containsExactly(PACKAGE_1);
+        assertThat(shadowTask.getPendingFullBackups()).isEmpty();
+        // TODO: Assert more about KeyValueBackupTask
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#requestBackup(String[], IBackupObserver,
+     * int)} returns {@link BackupManager#SUCCESS} and updates bookkeeping if backup for a full
+     * backup package succeeds.
+     */
+    @Test
+    @Config(shadows = ShadowKeyValueBackupTask.class)
+    public void testRequestBackup_whenPackageIsFullBackup() throws Exception {
+        setUpForRequestBackup(PACKAGE_1);
+        ShadowAppBackupUtils.setAppGetsFullBackup(PACKAGE_1);
+        UserBackupManagerService backupManagerService =
+                createBackupManagerServiceForRequestBackup();
+
+        int result = backupManagerService.requestBackup(new String[] {PACKAGE_1}, mObserver, 0);
+
+        mShadowBackupLooper.runToEndOfTasks();
+        assertThat(result).isEqualTo(BackupManager.SUCCESS);
+        ShadowKeyValueBackupTask shadowTask = ShadowKeyValueBackupTask.getLastCreated();
+        assertThat(shadowTask.getQueue()).isEmpty();
+        assertThat(shadowTask.getPendingFullBackups()).containsExactly(PACKAGE_1);
+        // TODO: Assert more about KeyValueBackupTask
+        tearDownForRequestBackup();
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#backupNow()} clears the calling identity
+     * for scheduling a job and then restores the original calling identity after the operation.
+     */
+    @Test
+    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJob.class})
+    public void testBackupNow_clearsCallingIdentityForJobScheduler() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        setUpPowerManager(backupManagerService);
+        ShadowBinder.setCallingUid(1);
+
+        backupManagerService.backupNow();
+
+        assertThat(ShadowKeyValueBackupJob.getCallingUid()).isEqualTo(ShadowBinder.LOCAL_UID);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#backupNow()} restores the original
+     * calling identity if an exception is thrown during execution.
+     */
+    @Test
+    @Config(shadows = {ShadowBinder.class, ShadowKeyValueBackupJobException.class})
+    public void testBackupNow_whenExceptionThrown_restoresCallingIdentity() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        setUpPowerManager(backupManagerService);
+        ShadowBinder.setCallingUid(1);
+
+        expectThrows(IllegalArgumentException.class, backupManagerService::backupNow);
+        assertThat(ShadowKeyValueBackupJobException.getCallingUid())
+                .isEqualTo(ShadowBinder.LOCAL_UID);
+        assertThat(Binder.getCallingUid()).isEqualTo(1);
+    }
+
+    private UserBackupManagerService createBackupManagerServiceForRequestBackup() {
+        UserBackupManagerService backupManagerService = createInitializedBackupManagerService();
+        backupManagerService.setEnabled(true);
+        backupManagerService.setProvisioned(true);
+        return backupManagerService;
+    }
+
+    /**
+     * Test verifying that {@link UserBackupManagerService#UserBackupManagerService(Context,
+     * Trampoline, HandlerThread, File, File, TransportManager)} posts a transport registration task
+     * to the backup handler thread.
+     */
+    @Test
+    public void testConstructor_postRegisterTransports() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+        createBackupManagerService();
+
+        mShadowBackupLooper.runToEndOfTasks();
+        verify(mTransportManager).registerTransports();
+    }
+
+    /**
+     * Test verifying that the {@link UserBackupManagerService#UserBackupManagerService(Context,
+     * Trampoline, HandlerThread, File, File, TransportManager)} does not directly register
+     * transports in its own thread.
+     */
+    @Test
+    public void testConstructor_doesNotRegisterTransportsSynchronously() {
+        mShadowContext.grantPermissions(android.Manifest.permission.BACKUP);
+
+        createBackupManagerService();
+
+        // Operations posted to mBackupThread only run with mShadowBackupLooper.runToEndOfTasks()
+        verify(mTransportManager, never()).registerTransports();
+    }
+
+    private UserBackupManagerService createBackupManagerService() {
+        return new UserBackupManagerService(
+                mContext,
+                new Trampoline(mContext),
+                mBackupThread,
+                mBaseStateDir,
+                mDataDir,
+                mTransportManager);
+    }
+
+    private UserBackupManagerService createInitializedBackupManagerService() {
+        return BackupManagerServiceTestUtils.createInitializedUserBackupManagerService(
+                mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
+    }
+
+    private void setUpPowerManager(UserBackupManagerService backupManagerService) {
+        PowerManager powerManagerMock = mock(PowerManager.class);
+        when(powerManagerMock.getPowerSaveState(anyInt()))
+                .thenReturn(new PowerSaveState.Builder().setBatterySaverEnabled(true).build());
+        backupManagerService.setPowerManager(powerManagerMock);
+    }
+
+    /**
+     * We can't mock the void method {@link #schedule(Context, long, BackupManagerConstants)} so we
+     * extend {@link ShadowKeyValueBackupJob} and throw an exception at the end of the method.
+     */
+    @Implements(KeyValueBackupJob.class)
+    public static class ShadowKeyValueBackupJobException extends ShadowKeyValueBackupJob {
+        /**
+         * Implementation of {@link ShadowKeyValueBackupJob#schedule(Context, long,
+         * BackupManagerConstants)} that throws an {@link IllegalArgumentException}.
+         */
+        public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
+            ShadowKeyValueBackupJob.schedule(ctx, delay, constants);
+            throw new IllegalArgumentException();
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index fd7ced2..423512c 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -1,10 +1,10 @@
 package com.android.server.backup.fullbackup;
 
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
index 6ee6eb6..a14cc51 100644
--- a/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/internal/PerformInitializeTaskTest.java
@@ -46,6 +46,7 @@
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -71,7 +72,7 @@
 @Config(shadows = ShadowSlog.class)
 @Presubmit
 public class PerformInitializeTaskTest {
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private TransportManager mTransportManager;
     @Mock private OnTaskFinishedListener mListener;
     @Mock private IBackupTransport mTransportBinder;
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
index a0afb5e..a1b8a95 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupReporterTest.java
@@ -26,7 +26,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.testing.shadows.ShadowEventLog;
 import com.android.server.testing.shadows.ShadowSlog;
 
@@ -41,7 +41,7 @@
 @Config(shadows = {ShadowEventLog.class, ShadowSlog.class})
 @Presubmit
 public class KeyValueBackupReporterTest {
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private IBackupObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
 
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index a69f007..1aa4999 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -26,7 +26,7 @@
 
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils.createBackupWakeLock;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
-        .createInitializedBackupManagerService;
+        .createInitializedUserBackupManagerService;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
         .setUpBackupManagerServiceBasics;
 import static com.android.server.backup.testing.BackupManagerServiceTestUtils
@@ -103,12 +103,12 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.BackupHandler;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.remote.RemoteCall;
@@ -178,7 +178,7 @@
     @Mock private IBackupObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
     @Mock private OnTaskFinishedListener mListener;
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     private TransportData mTransport;
     private ShadowLooper mShadowBackupLooper;
     private Handler mBackupHandler;
@@ -227,7 +227,7 @@
         setUpBinderCallerAndApplicationAsSystem(mApplication);
         mBackupManagerService =
                 spy(
-                        createInitializedBackupManagerService(
+                        createInitializedUserBackupManagerService(
                                 mContext, mBaseStateDir, mDataDir, mTransportManager));
         setUpBackupManagerServiceBasics(
                 mBackupManagerService,
@@ -720,7 +720,7 @@
     }
 
     /**
-     * Agent unavailable means {@link BackupManagerService#bindToAgentSynchronous(ApplicationInfo,
+     * Agent unavailable means {@link UserBackupManagerService#bindToAgentSynchronous(ApplicationInfo,
      * int)} returns {@code null}.
      *
      * @see #setUpAgent(PackageData)
@@ -2597,7 +2597,7 @@
      *
      * <ul>
      *   <li>The transport being initialized with {@link IBackupTransport#initializeDevice()}
-     *   <li>{@link BackupManagerService#resetBackupState(File)} being called, which will:
+     *   <li>{@link UserBackupManagerService#resetBackupState(File)} being called, which will:
      *       <ul>
      *         <li>Reset processed packages journal.
      *         <li>Reset current token to 0.
@@ -2617,7 +2617,7 @@
 
     /**
      * Forces transport initialization and call to {@link
-     * BackupManagerService#resetBackupState(File)}
+     * UserBackupManagerService#resetBackupState(File)}
      */
     private void deletePmStateFile() throws IOException {
         Files.deleteIfExists(getStateFile(mTransport, PM_PACKAGE));
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 0e2b95b..859392d 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,8 +51,8 @@
 
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.BackupHandler;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils;
@@ -85,7 +85,7 @@
     private static final long TOKEN_1 = 1L;
     private static final long TOKEN_2 = 2L;
 
-    @Mock private BackupManagerService mBackupManagerService;
+    @Mock private UserBackupManagerService mBackupManagerService;
     @Mock private TransportManager mTransportManager;
     @Mock private IRestoreObserver mObserver;
     @Mock private IBackupManagerMonitor mMonitor;
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index f307730..bacc44e 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,9 +37,9 @@
 import android.util.Log;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.Trampoline;
 import com.android.server.backup.TransportManager;
+import com.android.server.backup.UserBackupManagerService;
 
 import org.mockito.stubbing.Answer;
 import org.robolectric.shadows.ShadowApplication;
@@ -49,26 +49,26 @@
 import java.lang.Thread.UncaughtExceptionHandler;
 import java.util.concurrent.atomic.AtomicReference;
 
-/** Test utils for {@link BackupManagerService} and friends. */
+/** Test utils for {@link UserBackupManagerService} and friends. */
 public class BackupManagerServiceTestUtils {
     /**
      * If the class-under-test is going to execute methods as the system, it's a good idea to also
      * call {@link #setUpBinderCallerAndApplicationAsSystem(Application)} before this method.
      */
-    public static BackupManagerService createInitializedBackupManagerService(
+    public static UserBackupManagerService createInitializedUserBackupManagerService(
             Context context, File baseStateDir, File dataDir, TransportManager transportManager) {
-        return createInitializedBackupManagerService(
+        return createInitializedUserBackupManagerService(
                 context, startBackupThread(null), baseStateDir, dataDir, transportManager);
     }
 
-    public static BackupManagerService createInitializedBackupManagerService(
+    public static UserBackupManagerService createInitializedUserBackupManagerService(
             Context context,
             HandlerThread backupThread,
             File baseStateDir,
             File dataDir,
             TransportManager transportManager) {
-        BackupManagerService backupManagerService =
-                new BackupManagerService(
+        UserBackupManagerService backupManagerService =
+                new UserBackupManagerService(
                         context,
                         new Trampoline(context),
                         backupThread,
@@ -80,15 +80,16 @@
     }
 
     /**
-     * Sets up basic mocks for {@link BackupManagerService} mock. If {@code backupManagerService} is
-     * a spy, make sure you provide in the arguments the same objects that the original object uses.
+     * Sets up basic mocks for {@link UserBackupManagerService} mock. If {@code
+     * backupManagerService} is a spy, make sure you provide in the arguments the same objects that
+     * the original object uses.
      *
      * <p>If the class-under-test is going to execute methods as the system, it's a good idea to
      * also call {@link #setUpBinderCallerAndApplicationAsSystem(Application)}.
      */
     @SuppressWarnings("ResultOfMethodCallIgnored")
     public static void setUpBackupManagerServiceBasics(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             Application application,
             TransportManager transportManager,
             PackageManager packageManager,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index ca80664..ac5d2da 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -18,8 +18,8 @@
 
 import android.annotation.Nullable;
 
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.keyvalue.KeyValueBackupReporter;
 import com.android.server.backup.keyvalue.KeyValueBackupTask;
@@ -54,7 +54,7 @@
 
     @Implementation
     protected void __constructor__(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             String transportDirName,
             List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 228d4eb..2cebbeb 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,7 +21,7 @@
 import android.app.backup.IRestoreObserver;
 import android.content.pm.PackageInfo;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.internal.OnTaskFinishedListener;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
 import com.android.server.backup.transport.TransportClient;
@@ -47,7 +47,7 @@
         sLastShadow = null;
     }
 
-    private BackupManagerService mBackupManagerService;
+    private UserBackupManagerService mBackupManagerService;
     @Nullable private PackageInfo mPackage;
     private boolean mIsFullSystemRestore;
     @Nullable private String[] mFilterSet;
@@ -55,7 +55,7 @@
 
     @Implementation
     protected void __constructor__(
-            BackupManagerService backupManagerService,
+            UserBackupManagerService backupManagerService,
             TransportClient transportClient,
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index fa17b61..746c453 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -143,9 +143,6 @@
         <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
         <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
 
-        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
-                  android:showWhenLocked="true"/>
-
         <activity android:name="com.android.server.pm.ShortcutTestActivity"
                  android:enabled="true" android:exported="true" />
 
@@ -206,12 +203,6 @@
             </intent-filter>
         </activity-alias>
 
-        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
-        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
-        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
-        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskChangeCallbacks" />
-        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityTaskDescriptionChange" />
-
         <receiver android:name="com.android.server.appwidget.DummyAppWidget">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/services/tests/servicestests/res/raw/input_port_associations.xml
new file mode 100644
index 0000000..b10d541
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations.xml
@@ -0,0 +1,4 @@
+<ports>
+    <port display="0" input="USB1" />
+    <port display="1" input="USB2" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
new file mode 100644
index 0000000..8eeb1f5
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml
@@ -0,0 +1,3 @@
+<ports>
+    <port display="a" input="USB1" />
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
new file mode 100644
index 0000000..cf6e124
--- /dev/null
+++ b/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml
@@ -0,0 +1,3 @@
+<ports>
+    <port Garbage data inside xml>
+</ports>
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/shortcut_share_targets.xml b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
new file mode 100644
index 0000000..ec696e9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_share_targets.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 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.
+  -->
+
+<!-- Test XML resource to read share-targets from, used in ShortcutManagerTest1.java -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+    <shortcut
+        android:shortcutId="dummy_shortcut1"
+        android:enabled="true"
+        android:shortcutShortLabel="@string/shortcut_title1">
+        <intent
+            android:action="android.intent.action.VIEW"
+            android:targetPackage="com.test.somepackage"
+            android:targetClass="com.test.somepackage.someclass" />
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
+
+    <!-- Valid share target definition -->
+    <share-target android:targetClass="com.test.directshare.TestActivity1">
+        <data
+            android:scheme="http"
+            android:host="www.google.com"
+            android:port="1234"
+            android:path="somePath"
+            android:pathPrefix="somePathPrefix"
+            android:pathPattern="somePathPattern"
+            android:mimeType="text/plain"/>
+        <category android:name="com.test.category.CATEGORY1"/>
+        <category android:name="com.test.category.CATEGORY2"/>
+    </share-target>
+
+    <!-- Share target missing data tag, will be dropped -->
+    <share-target android:targetClass="com.test.directshare.TestActivity">
+        <category android:name="com.test.category.CATEGORY2"/>
+    </share-target>
+
+    <!-- Share target missing target class, will be dropped -->
+    <share-target>
+        <data
+            android:scheme="file"
+            android:host="www.somehost.com"
+            android:port="1234"
+            android:mimeType="video/*"/>
+        <category android:name="com.test.category.CATEGORY3"/>
+    </share-target>
+
+    <shortcut
+        android:shortcutId="dummy_shortcut2"
+        android:enabled="true"
+        android:shortcutShortLabel="@string/shortcut_title1">
+        <intent
+            android:action="android.intent.action.VIEW"
+            android:targetPackage="com.test.somepackage"
+            android:targetClass="com.test.somepackage.someclass" />
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
+
+    <!-- Share target missing category, will be dropped -->
+    <share-target android:targetClass="com.test.directshare.TestActivity">
+        <data
+            android:scheme="content"
+            android:mimeType="text/plain"/>
+    </share-target>
+
+    <!-- Valid share target definition -->
+    <share-target android:targetClass="com.test.directshare.TestActivity5">
+        <category android:name="com.test.category.CATEGORY5"/>
+        <category android:name="com.test.category.CATEGORY6"/>
+        <data android:mimeType="video/mp4"/>
+        <data
+            android:scheme="content"
+            android:mimeType="video/*"/>
+    </share-target>
+</shortcuts>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
index 81107cf..2a78b6f 100644
--- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -17,9 +17,10 @@
 package com.android.server;
 
 
-import static org.mockito.Mockito.when;
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
@@ -134,4 +135,44 @@
         mContext.sendBroadcast(intentUnplugged);
         assertThat(deviceState.isCharging()).isFalse();
     }
+
+    @Test
+    public void correctlyTracksTimeOnBattery() throws Exception {
+        CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+        when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+        service.onStart();
+        CachedDeviceState.Readonly deviceState =
+                LocalServices.getService(CachedDeviceState.Readonly.class);
+
+        CachedDeviceState.TimeInStateStopwatch stopwatch =
+                deviceState.createTimeOnBatteryStopwatch();
+
+        // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+        assertThat(stopwatch.isRunning()).isFalse();
+        service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+        assertThat(stopwatch.isRunning()).isTrue();
+        stopwatch.reset();
+
+        Thread.sleep(100);
+        assertThat(stopwatch.isRunning()).isTrue();
+        assertThat(stopwatch.getMillis()).isAtLeast(100L);
+
+        long timeOnBatteryBeforePluggedIn = stopwatch.getMillis();
+        Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        mContext.sendBroadcast(intentPluggedIn);
+
+        assertThat(stopwatch.getMillis()).isAtLeast(timeOnBatteryBeforePluggedIn);
+        assertThat(stopwatch.isRunning()).isFalse();
+
+        long timeOnBatteryAfterPluggedIn = stopwatch.getMillis();
+        Thread.sleep(20);
+        assertThat(stopwatch.getMillis()).isEqualTo(timeOnBatteryAfterPluggedIn);
+
+        stopwatch.reset();
+        assertThat(stopwatch.getMillis()).isEqualTo(0L);
+        assertThat(stopwatch.isRunning()).isFalse();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index c7409d7..89c7b71 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -20,7 +20,6 @@
 import static com.android.server.am.MemoryStatUtil.JIFFY_NANOS;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.PAGE_SIZE;
-import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
 import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
@@ -197,23 +196,6 @@
     }
 
     @Test
-    public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() {
-        assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234"));
-    }
-
-    @Test
-    public void testParseMemoryMaxUsageFromMemCg_emptyContents() {
-        assertEquals(0, parseMemoryMaxUsageFromMemCg(""));
-
-        assertEquals(0, parseMemoryMaxUsageFromMemCg(null));
-    }
-
-    @Test
-    public void testParseMemoryMaxUsageFromMemCg_incorrectValue() {
-        assertEquals(0, parseMemoryMaxUsageFromMemCg("memory"));
-    }
-
-    @Test
     public void testParseMemoryStatFromProcfs_parsesCorrectValues() {
         MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
         assertEquals(1, stat.pgfault);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d52051eec..479a19b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -34,7 +34,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.testutils.PackageManagerStub;
 
 import org.junit.Before;
@@ -97,7 +97,7 @@
         applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
         applicationInfo.uid = Process.SYSTEM_UID;
         applicationInfo.backupAgentName = CUSTOM_BACKUP_AGENT_NAME;
-        applicationInfo.packageName = BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
+        applicationInfo.packageName = UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 
         boolean isEligible = AppBackupUtils.appIsEligibleForBackup(applicationInfo,
                 mPackageManagerStub);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 4774985..d43b677 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -51,8 +51,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.FileMetadata;
+import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.restore.PerformAdbRestoreTask;
 import com.android.server.backup.restore.RestorePolicy;
 import com.android.server.backup.testutils.PackageManagerStub;
@@ -150,7 +150,7 @@
 
         assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
         assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
-        assertThat(fileMetadata.path).isEqualTo(BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        assertThat(fileMetadata.path).isEqualTo(UserBackupManagerService.BACKUP_MANIFEST_FILENAME);
 
         tarBackupReader.skipTarPadding(fileMetadata.size);
 
diff --git a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
new file mode 100644
index 0000000..636aa37
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 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.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.util.Pair;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * atest ConfigurationProcessorTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationProcessorTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    @Test
+    public void testGetInputPortAssociations() {
+        final int res = com.android.frameworks.servicestests.R.raw.input_port_associations;
+        InputStream xml = mContext.getResources().openRawResource(res);
+        List<Pair<String, String>> associations = null;
+        try {
+            associations = ConfigurationProcessor.processInputPortAssociations(xml);
+        } catch (Exception e) {
+            fail("Could not process xml file for input associations");
+        }
+        assertNotNull(associations);
+        assertEquals(2, associations.size());
+        assertTrue(associations.contains(Pair.create("USB1", "0")));
+        assertTrue(associations.contains(Pair.create("USB2", "1")));
+    }
+
+    @Test
+    public void testGetInputPortAssociationsBadDisplayport() {
+        final int res =
+                com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport;
+        InputStream xml = mContext.getResources().openRawResource(res);
+        List<Pair<String, String>> associations = null;
+        try {
+            associations = ConfigurationProcessor.processInputPortAssociations(xml);
+        } catch (Exception e) {
+            fail("Could not process xml file for input associations");
+        }
+        assertNotNull(associations);
+        assertEquals(0, associations.size());
+    }
+
+    @Test
+    public void testGetInputPortAssociationsEmptyConfig() {
+        final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml;
+        InputStream xml = mContext.getResources().openRawResource(res);
+        try {
+            ConfigurationProcessor.processInputPortAssociations(xml);
+            fail("Parsing should fail, because xml contains bad data");
+        } catch (Exception e) {
+            // This is expected
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
new file mode 100644
index 0000000..fc2dcb9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SP800DeriveTests.java
@@ -0,0 +1,40 @@
+/*
+ * 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.locksettings;
+
+import android.test.AndroidTestCase;
+
+import com.android.internal.util.HexDump;
+
+public class SP800DeriveTests extends AndroidTestCase {
+    public void testFixedInput() throws Exception {
+        // CAVP: https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/key-derivation
+        byte[] keyBytes = HexDump.hexStringToByteArray(
+            "e204d6d466aad507ffaf6d6dab0a5b26"
+            + "152c9e21e764370464e360c8fbc765c6");
+        SP800Derive sk = new SP800Derive(keyBytes);
+        byte[] fixedInput = HexDump.hexStringToByteArray(
+            "7b03b98d9f94b899e591f3ef264b71b1"
+            + "93fba7043c7e953cde23bc5384bc1a62"
+            + "93580115fae3495fd845dadbd02bd645"
+            + "5cf48d0f62b33e62364a3a80");
+        byte[] res = sk.fixedInput(fixedInput);
+        assertEquals((
+                "770dfab6a6a4a4bee0257ff335213f78"
+                + "d8287b4fd537d5c1fffa956910e7c779").toUpperCase(), HexDump.toHexString(res));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 113ee2d..99b827c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.net;
 
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -72,7 +72,6 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
-import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -142,12 +141,14 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
-import com.android.server.net.NetworkPolicyManagerInternal;
-import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkStatsManagerInternal;
+import com.android.server.DeviceIdleController;
+import com.android.server.LocalServices;
 
 import com.google.common.util.concurrent.AbstractFuture;
 
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -162,9 +163,6 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
@@ -195,15 +193,6 @@
 
 /**
  * Tests for {@link NetworkPolicyManagerService}.
- *
- * <p>Typical usage:
- *
- * <pre><code>
-    m -j32 FrameworksServicesTests && adb install -r -g \
-    ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \
-    adb shell am instrument -e class "com.android.server.NetworkPolicyManagerServiceTest" -w \
-    "com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner"
- * </code></pre>
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -376,7 +365,7 @@
                 return null;
             }
         }).when(mActivityManager).registerUidObserver(any(), anyInt(),
-                eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), isNull(String.class));
+                eq(NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
@@ -425,7 +414,7 @@
 
         // catch INetworkManagementEventObserver during systemReady()
         final ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
-              ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
+                ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
@@ -843,6 +832,18 @@
         assertTrue(mService.isUidForeground(UID_B));
     }
 
+    @Test
+    public void testAppIdleTempWhitelisting() throws Exception {
+        mService.setAppIdleWhitelist(UID_A, true);
+        mService.setAppIdleWhitelist(UID_B, false);
+        int[] whitelistedIds = mService.getAppIdleWhitelist();
+        assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0);
+        assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0);
+        assertFalse(mService.isUidIdle(UID_A));
+        // Can't currently guarantee UID_B's app idle state.
+        // TODO: expand with multiple app idle states.
+    }
+
     private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
         RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
                 ZoneId.systemDefault());
@@ -1770,7 +1771,7 @@
     }
 
     private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes,
-            long limitBytes, boolean inferred){
+            long limitBytes, boolean inferred) {
         final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID);
         return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes,
                 limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred);
@@ -1868,7 +1869,7 @@
     }
 
     private static void assertNotificationType(int expected, String actualTag) {
-        assertEquals("notification type mismatch for '" + actualTag +"'",
+        assertEquals("notification type mismatch for '" + actualTag + "'",
                 Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
     }
 
@@ -1902,7 +1903,8 @@
         final String action = ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
         final Intent intent = future.get(5, TimeUnit.SECONDS);
         assertNotNull("Didn't get a " + action + "intent in 5 seconds");
-        assertEquals("Wrong package on " + action + " intent", expectedPackage, intent.getPackage());
+        assertEquals("Wrong package on " + action + " intent",
+                expectedPackage, intent.getPackage());
     }
 
     // TODO: replace by Truth, Hamcrest, or a similar tool.
@@ -1923,7 +1925,7 @@
         }
         if (errors.length() > 0) {
             fail("assertContainsInAnyOrder(expected=" + Arrays.toString(expected)
-                    + ", actual=" + Arrays.toString(actual) +") failed: \n" + errors);
+                    + ", actual=" + Arrays.toString(actual) + ") failed: \n" + errors);
         }
     }
 
@@ -1986,7 +1988,7 @@
 
         @Override
         public Void answer(InvocationOnMock invocation) throws Throwable {
-            Log.d(TAG,"counting down on answer: " + invocation);
+            Log.d(TAG, "counting down on answer: " + invocation);
             latch.countDown();
             return null;
         }
@@ -2024,8 +2026,8 @@
             final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
             final File netConfigFile = new File(mPolicyDir, "netpolicy.xml");
             Log.d(TAG, "Creating " + netConfigFile + " from asset " + assetPath);
-            try (final InputStream in = context.getResources().getAssets().open(assetPath);
-                    final OutputStream out = new FileOutputStream(netConfigFile)) {
+            try (InputStream in = context.getResources().getAssets().open(assetPath);
+                    OutputStream out = new FileOutputStream(netConfigFile)) {
                 Streams.copy(in, out);
             }
         }
@@ -2037,9 +2039,7 @@
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
     public @interface NetPolicyXml {
-
-        public String value() default "";
-
+        String value() default "";
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a3348c2..1f5c64e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -91,8 +91,8 @@
 import com.android.server.SystemService;
 import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
 import com.android.server.pm.ShortcutUser.PackageWithUser;
-
 import com.android.server.wm.ActivityTaskManagerInternal;
+
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
 import org.mockito.invocation.InvocationOnMock;
@@ -105,8 +105,6 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -762,6 +760,7 @@
         LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
         LocalServices.addService(ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
@@ -963,6 +962,10 @@
         return getInstrumentation().getContext();
     }
 
+    protected Context getClientContext() {
+        return mClientContext;
+    }
+
     protected ShortcutManager getManager() {
         return mManager;
     }
@@ -1792,6 +1795,15 @@
     }
 
     /**
+     * @return all share targets stored internally for the caller.
+     */
+    protected List<ShareTargetInfo> getCallerShareTargets() {
+        final ShortcutPackage p = mService.getPackageShortcutForTest(
+                getCallingPackage(), getCallingUserId());
+        return p == null ? null : p.getAllShareTargetsForTest();
+    }
+
+    /**
      * @return all shortcuts owned by caller that are actually visible via ShortcutManager.
      * See also {@link #getCallerShortcuts}.
      */
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index d798865..ce59e6e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -31,6 +31,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
+import android.content.pm.UsesPermissionInfo;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -464,6 +465,7 @@
         pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
         pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
         pkg.requestedPermissions.add("foo7");
+        pkg.usesPermissionInfos.add(new UsesPermissionInfo("foo7"));
         pkg.implicitPermissions.add("foo25");
 
         pkg.protectedBroadcasts = new ArrayList<>();
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 fa73447..3172afb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -106,6 +106,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.function.BiConsumer;
@@ -398,7 +399,7 @@
         assertEquals(3, mManager.getRemainingCallCount());
     }
 
-   public void testPublishWithNoActivity() {
+    public void testPublishWithNoActivity() {
         // If activity is not explicitly set, use the default one.
 
         mRunningUsers.put(USER_10, true);
@@ -8015,4 +8016,56 @@
 
         assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
     }
+
+    public void testParseShareTargetsFromManifest() {
+        // These values must exactly match the content of shortcuts_share_targets.xml resource
+        List<ShareTargetInfo> expectedValues = new ArrayList<>();
+        expectedValues.add(new ShareTargetInfo(
+                new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
+                        "http", "www.google.com", "1234", "somePath", "somePathPattern",
+                        "somePathPrefix", "text/plain")}, "com.test.directshare.TestActivity1",
+                new String[]{"com.test.category.CATEGORY1", "com.test.category.CATEGORY2"}));
+        expectedValues.add(new ShareTargetInfo(new ShareTargetInfo.TargetData[]{
+                new ShareTargetInfo.TargetData(null, null, null, null, null, null, "video/mp4"),
+                new ShareTargetInfo.TargetData("content", null, null, null, null, null, "video/*")},
+                "com.test.directshare.TestActivity5",
+                new String[]{"com.test.category.CATEGORY5", "com.test.category.CATEGORY6"}));
+
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_share_targets);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        List<ShareTargetInfo> shareTargets = getCallerShareTargets();
+
+        assertNotNull(shareTargets);
+        assertEquals(expectedValues.size(), shareTargets.size());
+
+        for (int i = 0; i < expectedValues.size(); i++) {
+            ShareTargetInfo expected = expectedValues.get(i);
+            ShareTargetInfo actual = shareTargets.get(i);
+
+            assertEquals(expected.mTargetData.length, actual.mTargetData.length);
+            for (int j = 0; j < expected.mTargetData.length; j++) {
+                assertEquals(expected.mTargetData[j].mScheme, actual.mTargetData[j].mScheme);
+                assertEquals(expected.mTargetData[j].mHost, actual.mTargetData[j].mHost);
+                assertEquals(expected.mTargetData[j].mPort, actual.mTargetData[j].mPort);
+                assertEquals(expected.mTargetData[j].mPath, actual.mTargetData[j].mPath);
+                assertEquals(expected.mTargetData[j].mPathPrefix,
+                        actual.mTargetData[j].mPathPrefix);
+                assertEquals(expected.mTargetData[j].mPathPattern,
+                        actual.mTargetData[j].mPathPattern);
+                assertEquals(expected.mTargetData[j].mMimeType, actual.mTargetData[j].mMimeType);
+            }
+
+            assertEquals(expected.mTargetClass, actual.mTargetClass);
+
+            assertEquals(expected.mCategories.length, actual.mCategories.length);
+            for (int j = 0; j < expected.mCategories.length; j++) {
+                assertEquals(expected.mCategories[j], actual.mCategories[j]);
+            }
+        }
+    }
 }
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 76d52fd..9b59f91 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -144,8 +144,8 @@
 
         assertExpectException(
                 IllegalArgumentException.class, "Short label must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
         });
@@ -153,15 +153,15 @@
         // same for add.
         assertExpectException(
                 IllegalArgumentException.class, "Short label must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
         });
 
         assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .setShortLabel("x")
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -169,8 +169,8 @@
 
         // same for add.
         assertExpectException(NullPointerException.class, "Intent must be provided", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                    .setActivity(new ComponentName(getTestContext().getPackageName(), "s"))
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                    .setActivity(new ComponentName(getClientContext().getPackageName(), "s"))
                     .setShortLabel("x")
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -178,7 +178,7 @@
 
         assertExpectException(
                 IllegalStateException.class, "does not belong to package", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
                     .setActivity(new ComponentName("xxx", "s"))
                     .build();
             assertTrue(getManager().setDynamicShortcuts(list(si)));
@@ -187,7 +187,7 @@
         // same for add.
         assertExpectException(
                 IllegalStateException.class, "does not belong to package", () -> {
-            ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
+            ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
                     .setActivity(new ComponentName("xxx", "s"))
                     .build();
             assertTrue(getManager().addDynamicShortcuts(list(si)));
@@ -198,24 +198,24 @@
 
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().setDynamicShortcuts(list(si)));
                 });
         // For add
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().addDynamicShortcuts(list(si)));
                 });
         // For update
         assertExpectException(
                 IllegalStateException.class, "is not main", () -> {
-                    ShortcutInfo si = new ShortcutInfo.Builder(getTestContext(), "id")
-                            .setActivity(new ComponentName(getTestContext(), "s"))
+                    ShortcutInfo si = new ShortcutInfo.Builder(getClientContext(), "id")
+                            .setActivity(new ComponentName(getClientContext(), "s"))
                             .build();
                     assertTrue(getManager().updateShortcuts(list(si)));
                 });
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
deleted file mode 100644
index 415b5d9..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * 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.wm;
-
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static android.content.res.Configuration.EMPTY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController;
-
-import org.junit.Test;
-
-/**
- * Test class for {@link AppWindowContainerController}.
- *
- * atest FrameworksServicesTests:AppWindowContainerControllerTests
- */
-@FlakyTest(bugId = 74078662)
-@SmallTest
-@Presubmit
-public class AppWindowContainerControllerTests extends WindowTestsBase {
-
-    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
-
-    @Test
-    public void testRemoveContainer() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert token was added to display.
-        assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was created and linked.
-        assertNotNull(controller.mContainer);
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-
-        // Assert token was remove from display.
-        assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder()));
-        // Assert that the container was removed.
-        assertNull(controller.mContainer);
-    }
-
-    @Test
-    public void testSetOrientation() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-
-        // Assert orientation is unspecified to start.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(),
-                EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */);
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation());
-
-        controller.removeContainer(mDisplayContent.getDisplayId());
-        // Assert orientation is unspecified to after container is removed.
-        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation());
-
-        // Reset display frozen state
-        mWm.mDisplayFrozen = false;
-    }
-
-    private void assertHasStartingWindow(AppWindowToken atoken) {
-        assertNotNull(atoken.startingSurface);
-        assertNotNull(atoken.startingData);
-        assertNotNull(atoken.startingWindow);
-    }
-
-    private void assertNoStartingWindow(AppWindowToken atoken) {
-        assertNull(atoken.startingSurface);
-        assertNull(atoken.startingWindow);
-        assertNull(atoken.startingData);
-        atoken.forAllWindows(windowState -> {
-            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
-        }, true);
-    }
-
-    @Test
-    public void testCreateRemoveStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller =
-                createAppWindowController();
-        controller.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent);
-        assertHasStartingWindow(atoken);
-        controller.removeStartingWindow();
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(atoken);
-    }
-
-    @Test
-    public void testAddRemoveRace() {
-        // There was once a race condition between adding and removing starting windows
-        for (int i = 0; i < 1000; i++) {
-            final WindowTestUtils.TestAppWindowContainerController controller =
-                    createAppWindowController();
-            controller.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                    false, false);
-            controller.removeStartingWindow();
-            waitUntilHandlersIdle();
-            assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent));
-
-            controller.getAppWindowToken(
-                    mDisplayContent).getParent().getParent().removeImmediately();
-        }
-    }
-
-    @Test
-    public void testTransferStartingWindow() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        controller2.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                true, true, false, true, false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTransferStartingWindowWhileCreating() {
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController();
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController();
-        ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> {
-
-            // Surprise, ...! Transfer window in the middle of the creation flow.
-            controller2.addStartingWindow(mPackageName,
-                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(),
-                    true, true, false, true, false, false);
-        });
-        controller1.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-        assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testTryTransferStartingWindowFromHiddenAboveToken() {
-
-        // Add two tasks on top of each other.
-        TestTaskWindowContainerController taskController =
-                new WindowTestUtils.TestTaskWindowContainerController(this);
-        final WindowTestUtils.TestAppWindowContainerController controllerTop =
-                createAppWindowController(taskController);
-        final WindowTestUtils.TestAppWindowContainerController controllerBottom =
-                createAppWindowController(taskController);
-
-        // Add a starting window.
-        controllerTop.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false, false);
-        waitUntilHandlersIdle();
-
-        // Make the top one invisible, and try transfering the starting window from the top to the
-        // bottom one.
-        controllerTop.setVisibility(false, false);
-        controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded();
-
-        // Assert that the bottom window now has the starting window.
-        assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent));
-        assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent));
-    }
-
-    @Test
-    public void testReparent() {
-        final StackWindowController stackController =
-            createStackControllerOnDisplay(mDisplayContent);
-        final WindowTestUtils.TestTaskWindowContainerController taskController1 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController1 =
-                createAppWindowController(taskController1);
-        final WindowTestUtils.TestTaskWindowContainerController taskController2 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-        final WindowTestUtils.TestAppWindowContainerController appWindowController2 =
-                createAppWindowController(taskController2);
-        final WindowTestUtils.TestTaskWindowContainerController taskController3 =
-                new WindowTestUtils.TestTaskWindowContainerController(stackController);
-
-        try {
-            appWindowController1.reparent(taskController1, 0);
-            fail("Should not be able to reparent to the same parent");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        try {
-            taskController3.setContainer(null);
-            appWindowController1.reparent(taskController3, 0);
-            fail("Should not be able to reparent to a task that doesn't have a container");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        // Reparent the app window and ensure that it is moved
-        appWindowController1.reparent(taskController2, 0);
-        assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent());
-        assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer)
-                .positionInParent());
-        assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer)
-                .positionInParent());
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController() {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(this));
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
deleted file mode 100644
index a91c5e7..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * Copyright (C) 2018 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.wm;
-
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
-import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
-import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import android.view.DisplayCutout;
-import android.view.DisplayInfo;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.wm.utils.WmDisplayCutout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
-
-    private DisplayFrames mFrames;
-    private WindowState mWindow;
-    private int mRotation = ROTATION_0;
-    private boolean mHasDisplayCutout;
-
-    @Before
-    public void setUp() throws Exception {
-        updateDisplayFrames();
-
-        mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
-        // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
-        // changing those frames.
-        doNothing().when(mWindow).computeFrameLw();
-
-        final WindowManager.LayoutParams attrs = mWindow.mAttrs;
-        attrs.width = MATCH_PARENT;
-        attrs.height = MATCH_PARENT;
-        attrs.flags =
-                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        attrs.format = PixelFormat.TRANSLUCENT;
-    }
-
-    public void setRotation(int rotation) {
-        mRotation = rotation;
-        updateDisplayFrames();
-    }
-
-    public void addDisplayCutout() {
-        mHasDisplayCutout = true;
-        updateDisplayFrames();
-    }
-
-    private void updateDisplayFrames() {
-        final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
-                mHasDisplayCutout);
-        mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
-    }
-
-    @Test
-    public void layoutWindowLw_appDrawsBars() {
-        mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars() {
-        mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
-        mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void addingWindow_doesNotTamperWithSysuiFlags() {
-        mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        addWindow(mWindow);
-
-        assertEquals(0, mWindow.mAttrs.systemUiVisibility);
-        assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout() {
-        addDisplayCutout();
-
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_never() {
-        addDisplayCutout();
-
-        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
-        addDisplayCutout();
-
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen() {
-        addDisplayCutout();
-
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
-        addDisplayCutout();
-
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
-        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
-    }
-
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_seascape() {
-        addDisplayCutout();
-        setRotation(ROTATION_270);
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
-                NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
-        addDisplayCutout();
-
-        mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
-        mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
-        mWindow.mAttrs.width = DISPLAY_WIDTH;
-        mWindow.mAttrs.height = DISPLAY_HEIGHT;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-    }
-
-    @Test
-    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
-        addDisplayCutout();
-        setRotation(ROTATION_90);
-
-        mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
-        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
-                DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-    }
-
-    @Test
-    public void layoutHint_appWindow() {
-        // Initialize DisplayFrames
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
-                false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
-                outDisplayCutout);
-
-        assertThat(outFrame, is(mFrames.mUnrestricted));
-        assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask() {
-        // Initialize DisplayFrames
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        final Rect taskBounds = new Rect(100, 100, 200, 200);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
-                false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
-                outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    @Test
-    public void layoutHint_appWindowInTask_outsideContentFrame() {
-        // Initialize DisplayFrames
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-
-        // Task is in the nav bar area (usually does not happen, but this is similar enough to the
-        // possible overlap with the IME)
-        final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
-                200, mFrames.mContent.bottom + 10);
-
-        final Rect outFrame = new Rect();
-        final Rect outContentInsets = new Rect();
-        final Rect outStableInsets = new Rect();
-        final Rect outOutsets = new Rect();
-        final DisplayCutout.ParcelableWrapper outDisplayCutout =
-                new DisplayCutout.ParcelableWrapper();
-
-        mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
-                true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets,
-                outDisplayCutout);
-
-        assertThat(outFrame, is(taskBounds));
-        assertThat(outContentInsets, is(new Rect()));
-        assertThat(outStableInsets, is(new Rect()));
-        assertThat(outOutsets, is(new Rect()));
-        assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
-    }
-
-    /**
-     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
-     *
-     * Convenience wrapper for when only the top and bottom inset are non-zero.
-     */
-    private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
-            int expectedInsetBottom) {
-        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
-    }
-
-    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
-    private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
-            int expectedInsetRight, int expectedInsetBottom) {
-        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
-                mFrames.mDisplayWidth - expectedInsetRight,
-                mFrames.mDisplayHeight - expectedInsetBottom), actual);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
deleted file mode 100644
index 60c0459..0000000
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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.wm;
-
-import static android.app.AppOpsManager.OP_NONE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
-import static com.android.server.wm.WindowContainer.POSITION_TOP;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.os.IBinder;
-import android.view.IApplicationToken;
-import android.view.IWindow;
-import android.view.SurfaceControl.Transaction;
-import android.view.WindowManager;
-
-import org.mockito.invocation.InvocationOnMock;
-
-/**
- * A collection of static functions that can be referenced by other test packages to provide access
- * to WindowManager related test functionality.
- */
-public class WindowTestUtils {
-    private static int sNextTaskId = 0;
-
-    /**
-     * Creates a mock instance of {@link StackWindowController}.
-     */
-    public static StackWindowController createMockStackWindowContainerController() {
-        StackWindowController controller = mock(StackWindowController.class);
-        controller.mContainer = mock(TestTaskStack.class);
-
-        // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
-        // to properly set bounds values in the configuration. We must mimick those actions here.
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Configuration config = invocationOnMock.<Configuration>getArgument(7);
-            final Rect bounds = invocationOnMock.<Rect>getArgument(0);
-            config.windowConfiguration.setBounds(bounds);
-            return null;
-        }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
-                anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
-
-        return controller;
-    }
-
-    /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
-    public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
-            int userId) {
-        synchronized (service.mGlobalLock) {
-            final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
-                    new ActivityManager.TaskDescription(), null);
-            stack.addTask(newTask, POSITION_TOP);
-            return newTask;
-        }
-    }
-
-    /**
-     * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
-     * normally be mocked out.
-     */
-    public static class TestTaskStack extends TaskStack {
-        TestTaskStack(WindowManagerService service, int stackId) {
-            super(service, stackId, null);
-        }
-
-        @Override
-        void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
-            // Do nothing.
-        }
-    }
-
-    static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
-        synchronized (dc.mService.mGlobalLock) {
-            return new TestAppWindowToken(dc);
-        }
-    }
-
-    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
-    public static class TestAppWindowToken extends AppWindowToken {
-        boolean mOnTop = false;
-        private Transaction mPendingTransactionOverride;
-
-        private TestAppWindowToken(DisplayContent dc) {
-            super(dc.mService, new IApplicationToken.Stub() {
-                public String getName() {return null;}
-                }, new ComponentName("", ""), false, dc, true /* fillsParent */);
-        }
-
-        TestAppWindowToken(WindowManagerService service, IApplicationToken token,
-                ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
-                long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
-                int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
-                boolean launchTaskBehind, boolean alwaysFocusable,
-                AppWindowContainerController controller) {
-            super(service, token, activityComponent, voiceInteraction, dc,
-                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
-                    orientation, rotationAnimationHint, configChanges, launchTaskBehind,
-                    alwaysFocusable, controller);
-        }
-
-        int getWindowsCount() {
-            return mChildren.size();
-        }
-
-        boolean hasWindow(WindowState w) {
-            return mChildren.contains(w);
-        }
-
-        WindowState getFirstChild() {
-            return mChildren.peekFirst();
-        }
-
-        WindowState getLastChild() {
-            return mChildren.peekLast();
-        }
-
-        int positionInParent() {
-            return getParent().mChildren.indexOf(this);
-        }
-
-        void setIsOnTop(boolean onTop) {
-            mOnTop = onTop;
-        }
-
-        @Override
-        boolean isOnTop() {
-            return mOnTop;
-        }
-
-        void setPendingTransaction(Transaction transaction) {
-            mPendingTransactionOverride = transaction;
-        }
-
-        @Override
-        public Transaction getPendingTransaction() {
-            return mPendingTransactionOverride == null
-                    ? super.getPendingTransaction()
-                    : mPendingTransactionOverride;
-        }
-    }
-
-    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
-        return createTestWindowToken(type, dc, false /* persistOnEmpty */);
-    }
-
-    static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
-            boolean persistOnEmpty) {
-        synchronized (dc.mService.mGlobalLock) {
-            return new TestWindowToken(type, dc, persistOnEmpty);
-        }
-    }
-
-    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
-    public static class TestWindowToken extends WindowToken {
-
-        private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
-            super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc,
-                    false /* ownerCanManageAppTokens */);
-        }
-
-        int getWindowsCount() {
-            return mChildren.size();
-        }
-
-        boolean hasWindow(WindowState w) {
-            return mChildren.contains(w);
-        }
-    }
-
-    /* Used so we can gain access to some protected members of the {@link Task} class */
-    public static class TestTask extends Task {
-        boolean mShouldDeferRemoval = false;
-        boolean mOnDisplayChangedCalled = false;
-        private boolean mIsAnimating = false;
-
-        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
-                int resizeMode, boolean supportsPictureInPicture,
-                TaskWindowContainerController controller) {
-            super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
-                    new ActivityManager.TaskDescription(), controller);
-        }
-
-        boolean shouldDeferRemoval() {
-            return mShouldDeferRemoval;
-        }
-
-        int positionInParent() {
-            return getParent().mChildren.indexOf(this);
-        }
-
-        @Override
-        void onDisplayChanged(DisplayContent dc) {
-            super.onDisplayChanged(dc);
-            mOnDisplayChangedCalled = true;
-        }
-
-        @Override
-        boolean isSelfAnimating() {
-            return mIsAnimating;
-        }
-
-        void setLocalIsAnimating(boolean isAnimating) {
-            mIsAnimating = isAnimating;
-        }
-    }
-
-    /**
-     * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
-     * class.
-     */
-    public static class TestTaskWindowContainerController extends TaskWindowContainerController {
-
-        static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
-            @Override
-            public void registerConfigurationChangeListener(
-                    ConfigurationContainerListener listener) {
-            }
-
-            @Override
-            public void unregisterConfigurationChangeListener(
-                    ConfigurationContainerListener listener) {
-            }
-
-            @Override
-            public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
-            }
-
-            @Override
-            public void requestResize(Rect bounds, int resizeMode) {
-            }
-        };
-
-        TestTaskWindowContainerController(WindowTestsBase testsBase) {
-            this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
-        }
-
-        TestTaskWindowContainerController(StackWindowController stackController) {
-            super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
-                    RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
-                    true /* showForAllUsers */, new ActivityManager.TaskDescription(),
-                    stackController.mService);
-        }
-
-        @Override
-        TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
-                boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
-            return new TestTask(taskId, stack, userId, mService, resizeMode,
-                    supportsPictureInPicture, this);
-        }
-    }
-
-    public static class TestAppWindowContainerController extends AppWindowContainerController {
-
-        final IApplicationToken mToken;
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController) {
-            this(taskController, new TestIApplicationToken());
-        }
-
-        TestAppWindowContainerController(TestTaskWindowContainerController taskController,
-                IApplicationToken token) {
-            super(taskController, token, new ComponentName("", "") /* activityComponent */,
-                    null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED,
-                    true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */,
-                    false /* voiceInteraction */, false /* launchTaskBehind */,
-                    false /* alwaysFocusable */, 0 /* targetSdkVersion */,
-                    0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */,
-                    taskController.mService);
-            mToken = token;
-        }
-
-        @Override
-        AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token,
-                ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
-                long inputDispatchingTimeoutNanos,
-                boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation,
-                int rotationAnimationHint, int configChanges, boolean launchTaskBehind,
-                boolean alwaysFocusable, AppWindowContainerController controller) {
-            return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc,
-                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
-                    orientation,
-                    rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable,
-                    controller);
-        }
-
-        AppWindowToken getAppWindowToken(DisplayContent dc) {
-            return (AppWindowToken) dc.getWindowToken(mToken.asBinder());
-        }
-    }
-
-    public static class TestIApplicationToken implements IApplicationToken {
-
-        private final Binder mBinder = new Binder();
-        @Override
-        public IBinder asBinder() {
-            return mBinder;
-        }
-        @Override
-        public String getName() {
-            return null;
-        }
-    }
-
-    /** Used to track resize reports. */
-    public static class TestWindowState extends WindowState {
-        boolean resizeReported;
-
-        TestWindowState(WindowManagerService service, Session session, IWindow window,
-                WindowManager.LayoutParams attrs, WindowToken token) {
-            super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
-                    false /* ownerCanAddInternalSystemWindow */);
-        }
-
-        @Override
-        void reportResized() {
-            super.reportResized();
-            resizeReported = true;
-        }
-
-        @Override
-        public boolean isGoneForLayoutLw() {
-            return false;
-        }
-
-        @Override
-        void updateResizingWindowIfNeeded() {
-            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
-            // the system that it can actually update the window.
-            boolean hadSurface = mHasSurface;
-            mHasSurface = true;
-
-            super.updateResizingWindowIfNeeded();
-
-            mHasSurface = hadSurface;
-        }
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9da204f..41d5a1c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -456,6 +456,31 @@
     }
 
     @Test
+    public void testNoBeepForImportanceDefaultInAutomotive() throws Exception {
+        mService.setIsAutomotive(true);
+
+        NotificationRecord r = getBeepyNotification();
+        r.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyNeverBeep();
+        assertFalse(r.isInterruptive());
+    }
+
+    @Test
+    public void testBeepForImportanceHighInAutomotive() throws Exception {
+        mService.setIsAutomotive(true);
+
+        NotificationRecord r = getBeepyNotification();
+
+        mService.buzzBeepBlinkLocked(r);
+
+        verifyBeepLooped();
+        assertTrue(r.isInterruptive());
+    }
+
+    @Test
     public void testNoInterruptionForMin() throws Exception {
         NotificationRecord r = getBeepyNotification();
         r.setSystemImportance(NotificationManager.IMPORTANCE_MIN);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index fcd29e1..d950360 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3711,4 +3711,23 @@
         verify(mAssistants).notifyAssistantSuggestedReplySent(
                 eq(r.sbn), eq(reply), eq(generatedByAssistant));
     }
+
+    @Test
+    public void testOnNotificationActionClick() {
+        final int actionIndex = 2;
+        final Notification.Action action =
+                new Notification.Action.Builder(null, "text", null).build();
+        final boolean generatedByAssistant = false;
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        NotificationVisibility notificationVisibility =
+                NotificationVisibility.obtain(r.getKey(), 1, 2, true);
+        mService.mNotificationDelegate.onNotificationActionClick(
+                10, 10, r.getKey(), actionIndex, action, notificationVisibility,
+                generatedByAssistant);
+        verify(mAssistants).notifyAssistantActionClicked(
+                eq(r.sbn), eq(actionIndex), eq(action), eq(generatedByAssistant));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 38d8e39..6c7ede3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -41,6 +41,7 @@
 
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
@@ -1097,6 +1098,25 @@
         assertFalse(Objects.equals(defaultRuleName, ruleAfterUpdating.name)); // update name
     }
 
+    @Test
+    public void testAddAutomaticZenRule() {
+        AutomaticZenRule zenRule = new AutomaticZenRule("name",
+                new ComponentName("android", "ScheduleConditionProvider"),
+                ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+                NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+        String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+
+        assertTrue(id != null);
+        ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
+        assertTrue(ruleInConfig != null);
+        assertEquals(zenRule.isEnabled(), ruleInConfig.enabled);
+        assertEquals(zenRule.isModified(), ruleInConfig.modified);
+        assertEquals(zenRule.getConditionId(), ruleInConfig.conditionId);
+        assertEquals(NotificationManager.zenModeFromInterruptionFilter(
+                zenRule.getInterruptionFilter(), -1), ruleInConfig.zenMode);
+        assertEquals(zenRule.getName(), ruleInConfig.name);
+    }
+
     private void setupZenConfig() {
         mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f128b4e2..3f3b996 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -34,9 +34,13 @@
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.REORDER_TASKS" />
 
     <application android:debuggable="true"
                  android:testOnly="true">
+        <uses-library android:name="android.test.mock" android:required="true" />
+
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityA" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityB" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityRequestedOrientationChange" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cb2a8ec..5bf3d2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -62,8 +62,9 @@
     @Test
     public void testLastFocusedStackIsUpdatedWhenMovingStack() {
         // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack stack =
+                new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build();
         final ActivityStack prevFocusedStack = display.getFocusedStack();
 
         stack.moveToFront("moveStackToFront");
@@ -83,7 +84,7 @@
     @Test
     public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
         // Create a pinned stack and move to front.
-        final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
                 .setStack(pinnedStack).build();
@@ -96,7 +97,7 @@
 
         // Create a fullscreen stack and move to front.
         final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
-                mSupervisor.getDefaultDisplay());
+                mRootActivityContainer.getDefaultDisplay());
         fullscreenStack.moveToFront("moveFullscreenStackToFront");
 
         // The focused stack should be the fullscreen stack.
@@ -138,7 +139,7 @@
         final ActivityDisplay display = spy(createNewActivityDisplay());
         doReturn(false).when(display).shouldDestroyContentOnRemove();
         doReturn(true).when(display).supportsSystemDecorations();
-        mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
 
         // Put home stack on the display.
         final ActivityStack homeStack = display.createStack(
@@ -175,14 +176,14 @@
      */
     @Test
     public void testTopRunningActivity() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = new StackBuilder(mSupervisor).build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         final ActivityRecord activity = stack.getTopActivity();
 
         // Create empty stack on top.
         final ActivityStack emptyStack =
-                new StackBuilder(mSupervisor).setCreateActivity(false).build();
+                new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
 
         // Make sure the top running activity is not affected when keyguard is not locked.
         assertTopRunningActivity(activity, display);
@@ -225,7 +226,7 @@
      */
     @Test
     public void testAlwaysOnTopStackLocation() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index c7f0521..cac9cf6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -24,20 +24,30 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.timeout;
 
 import android.content.Intent;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+
+import java.util.Arrays;
 
 /**
  * Tests for the {@link ActivityMetricsLaunchObserver} class.
@@ -51,6 +61,7 @@
 public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
     private ActivityMetricsLogger mActivityMetricsLogger;
     private ActivityMetricsLaunchObserver mLaunchObserver;
+    private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
 
     private TestActivityStack mStack;
     private TaskRecord mTask;
@@ -61,33 +72,62 @@
     public void setUpAMLO() throws Exception {
         setupActivityTaskManagerService();
 
-        mActivityMetricsLogger =
-                new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
-
         mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
 
-        // TODO: Use ActivityMetricsLaunchObserverRegistry .
-        java.lang.reflect.Field f =
-                mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
-        f.setAccessible(true);
-        f.set(mActivityMetricsLogger, mLaunchObserver);
+        // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
+        mActivityMetricsLogger = mSupervisor.getActivityMetricsLogger();
+
+        mLaunchObserverRegistry = mActivityMetricsLogger.getLaunchObserverRegistry();
+        mLaunchObserverRegistry.registerLaunchObserver(mLaunchObserver);
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mStack = mSupervisor.getDefaultDisplay().createStack(
+        mStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
         mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
         mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
+    @After
+    public void tearDownAMLO() throws Exception {
+        if (mLaunchObserverRegistry != null) {  // Don't NPE if setUp failed.
+            mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
+        }
+    }
+
+    static class ActivityRecordMatcher implements ArgumentMatcher</*@ActivityRecordProto*/ byte[]> {
+        private final @ActivityRecordProto byte[] mExpected;
+
+        public ActivityRecordMatcher(ActivityRecord activityRecord) {
+            mExpected = activityRecordToProto(activityRecord);
+        }
+
+        public boolean matches(@ActivityRecordProto byte[] actual) {
+            return Arrays.equals(mExpected, actual);
+        }
+    }
+
+    static @ActivityRecordProto byte[] activityRecordToProto(ActivityRecord record) {
+        return ActivityMetricsLogger.convertActivityRecordToProto(record);
+    }
+
+    static @ActivityRecordProto byte[] eqProto(ActivityRecord record) {
+        return argThat(new ActivityRecordMatcher(record));
+    }
+
+    static <T> T verifyAsync(T mock) {
+        // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
+        return verify(mock, timeout(100));
+    }
+
     @Test
     public void testOnIntentStarted() throws Exception {
         Intent intent = new Intent("action 1");
 
         mActivityMetricsLogger.notifyActivityLaunching(intent);
 
-        verify(mLaunchObserver).onIntentStarted(eq(intent));
+        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -102,7 +142,7 @@
         mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
                 activityRecord);
 
-        verify(mLaunchObserver).onIntentFailed();
+        verifyAsync(mLaunchObserver).onIntentFailed();
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -113,7 +153,7 @@
         mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
                 mActivityRecord);
 
-        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -127,7 +167,7 @@
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
                SystemClock.uptimeMillis());
 
-       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -135,12 +175,12 @@
     public void testOnActivityLaunchCancelled() throws Exception {
        testOnActivityLaunched();
 
-       mActivityRecord.nowVisible = true;
+       mActivityRecord.mDrawn = true;
 
        // Cannot time already-visible activities.
        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
 
-       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+       verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord));
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -151,7 +191,7 @@
         mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
                 mActivityRecord);
 
-        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+        verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt());
 
         // A second, distinct, activity launch is coalesced into the the current app launch sequence
         mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
@@ -170,7 +210,7 @@
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
                SystemClock.uptimeMillis());
 
-       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -178,13 +218,26 @@
     public void testOnActivityLaunchCancelledTrampoline() throws Exception {
        testOnActivityLaunchedTrampoline();
 
-       mActivityRecordTrampoline.nowVisible = true;
+       mActivityRecordTrampoline.mDrawn = true;
 
        // Cannot time already-visible activities.
        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
                mActivityRecordTrampoline);
 
-       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+       verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline));
        verifyNoMoreInteractions(mLaunchObserver);
     }
+
+    @Test
+    public void testActivityRecordProtoIsNotTooBig() throws Exception {
+        // The ActivityRecordProto must not be too big, otherwise converting it at runtime
+        // will become prohibitively expensive.
+        assertWithMessage("mActivityRecord: %s", mActivityRecord).
+                that(activityRecordToProto(mActivityRecord).length).
+                isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+
+        assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline).
+                that(activityRecordToProto(mActivityRecordTrampoline).length).
+                isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 170bd33..b6f1817 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,10 +29,9 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
 
-import static junit.framework.TestCase.assertNotNull;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -65,7 +64,7 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mStack = new StackBuilder(mSupervisor).build();
+        mStack = new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
     }
@@ -86,7 +85,7 @@
     public void testStackCleanupOnTaskRemoval() {
         mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING);
         // Stack should be gone on task removal.
-        assertNull(mService.mStackSupervisor.getStack(mStack.mStackId));
+        assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId));
     }
 
     @Test
@@ -116,7 +115,7 @@
         assertFalse(pauseFound.value);
 
         // Clear focused stack
-        final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
         when(display.getFocusedStack()).thenReturn(null);
 
         // In the unfocused stack, the activity should move to paused.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 8a6d587..f7b5d26 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -18,15 +18,8 @@
 
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
-import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -36,10 +29,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor
-        .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -54,12 +43,7 @@
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
 
-import android.app.ActivityOptions;
 import android.app.WaitResult;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.graphics.Rect;
-import android.os.Build;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.MediumTest;
@@ -67,8 +51,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.ArrayList;
-
 /**
  * Tests for the {@link ActivityStackSupervisor} class.
  *
@@ -83,78 +65,11 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
     }
 
     /**
-     * This test ensures that we do not try to restore a task based off an invalid task id. We
-     * should expect {@code null} to be returned in this case.
-     */
-    @Test
-    public void testRestoringInvalidTask() {
-        ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks();
-        TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
-                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
-        assertNull(task);
-    }
-
-    /**
-     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
-     * activity stack when a new task is added.
-     */
-    @Test
-    public void testReplacingTaskInPinnedStack() {
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord firstTask = firstActivity.getTask();
-
-        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(mFullscreenStack).build();
-        final TaskRecord secondTask = secondActivity.getTask();
-
-        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
-
-        // Ensure full screen stack has both tasks.
-        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
-
-        // Move first activity to pinned stack.
-        final Rect sourceBounds = new Rect();
-        mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
-                0f /*aspectRatio*/, "initialMove");
-
-        final ActivityDisplay display = mFullscreenStack.getDisplay();
-        ActivityStack pinnedStack = display.getPinnedStack();
-        // Ensure a task has moved over.
-        ensureStackPlacement(pinnedStack, firstTask);
-        ensureStackPlacement(mFullscreenStack, secondTask);
-
-        // Move second activity to pinned stack.
-        mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
-                0f /*aspectRatio*/, "secondMove");
-
-        // Need to get stacks again as a new instance might have been created.
-        pinnedStack = display.getPinnedStack();
-        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-        // Ensure stacks have swapped tasks.
-        ensureStackPlacement(pinnedStack, secondTask);
-        ensureStackPlacement(mFullscreenStack, firstTask);
-    }
-
-    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
-        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
-        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
-
-        if (tasks == null) {
-            return;
-        }
-
-        for (TaskRecord task : tasks) {
-            assertTrue(stackTasks.contains(task));
-        }
-    }
-
-    /**
      * Ensures that an activity is removed from the stopping activities list once it is resumed.
      */
     @Test
@@ -179,7 +94,7 @@
 
         // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
         // when this happens.
-        synchronized (mSupervisor.mService.mGlobalLock) {
+        synchronized (mService.mGlobalLock) {
             final WaitResult taskToFrontWait = new WaitResult();
             mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
             mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
@@ -195,337 +110,7 @@
 
             assertThat(mSupervisor.mWaitingActivityLaunched).isEmpty();
             assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
-            assertEquals(deliverToTopWait.who, firstActivity.realActivity);
+            assertEquals(deliverToTopWait.who, firstActivity.mActivityComponent);
         }
     }
-
-    @Test
-    public void testApplySleepTokensLocked() {
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final KeyguardController keyguard = mSupervisor.getKeyguardController();
-        final ActivityStack stack = mock(ActivityStack.class);
-        display.addChild(stack, 0 /* position */);
-
-        // Make sure we wake and resume in the case the display is turning on and the keyguard is
-        // not showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                true /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // showing.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                true /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Make sure we wake and don't resume when the display is turning on and the keyguard is
-        // not showing as unfocused.
-        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
-                false /* displayShouldSleep */, false /* isFocusedStack */,
-                false /* keyguardShowing */, true /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-
-        // Should not do anything if the display state hasn't changed.
-        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
-                false /* displayShouldSleep */, true /* isFocusedStack */,
-                false /* keyguardShowing */, false /* expectWakeFromSleep */,
-                false /* expectResumeTopActivity */);
-    }
-
-    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
-            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
-            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
-            boolean expectResumeTopActivity) {
-        reset(stack);
-
-        doReturn(displayShouldSleep).when(display).shouldSleep();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
-
-        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
-        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
-        mSupervisor.applySleepTokensLocked(true);
-        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
-        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
-                null /* target */, null /* targetOptions */);
-    }
-
-    /**
-     * Verifies that removal of activity with task and stack is done correctly.
-     */
-    @Test
-    public void testRemovingStackOnAppCrash() {
-        final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
-        final int originalStackCount = defaultDisplay.getChildCount();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
-
-        // Let's pretend that the app has crashed.
-        firstActivity.app.setThread(null);
-        mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
-
-        // Verify that the stack was removed.
-        assertEquals(originalStackCount, defaultDisplay.getChildCount());
-    }
-
-    @Test
-    public void testFocusability() {
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(stack).build();
-
-        // Under split screen primary we should be focusable when not minimized
-        mService.mStackSupervisor.setDockedStackMinimized(false);
-        assertTrue(stack.isFocusable());
-        assertTrue(activity.isFocusable());
-
-        // Under split screen primary we should not be focusable when minimized
-        mService.mStackSupervisor.setDockedStackMinimized(true);
-        assertFalse(stack.isFocusable());
-        assertFalse(activity.isFocusable());
-
-        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
-                .setStack(pinnedStack).build();
-
-        // We should not be focusable when in pinned mode
-        assertFalse(pinnedStack.isFocusable());
-        assertFalse(pinnedActivity.isFocusable());
-
-        // Add flag forcing focusability.
-        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
-
-        // We should not be focusable when in pinned mode
-        assertTrue(pinnedStack.isFocusable());
-        assertTrue(pinnedActivity.isFocusable());
-
-        // Without the overridding activity, stack should not be focusable.
-        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
-                REMOVE_TASK_MODE_DESTROYING);
-        assertFalse(pinnedStack.isFocusable());
-    }
-
-    /**
-     * Verify that split-screen primary stack will be chosen if activity is launched that targets
-     * split-screen secondary, but a matching existing instance is found on top of split-screen
-     * primary stack.
-     */
-    @Test
-    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
-        // Create primary split-screen stack with a task and an activity.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
-
-        // Find a launch stack for the top activity in split-screen primary, while requesting
-        // split-screen secondary.
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
-        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
-
-        // Assert that the primary stack is returned.
-        assertEquals(primaryStack, result);
-    }
-
-    /**
-     * Verify split-screen primary stack & task can resized by
-     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
-     */
-    @Test
-    public void testResizeDockedStackForSplitScreenPrimary() {
-        final Rect taskSize = new Rect(0, 0, 600, 600);
-        final Rect stackSize = new Rect(0, 0, 300, 300);
-
-        // Create primary split-screen stack with a task.
-        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
-
-        // Resize dock stack.
-        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
-
-        // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(primaryStack.getBounds(), stackSize);
-        assertEquals(task.getBounds(), taskSize);
-    }
-
-    /**
-     * Verify that home stack would be moved to front when the top activity is Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
-        final TaskRecord targetTask = targetStack.getChildAt(0);
-
-        // Create Recents on top of the display.
-        final ActivityStack stack =
-                new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify that home stack won't be moved to front if the top activity on other display is
-     * Recents.
-     */
-    @Test
-    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
-        // Create stack/task on default display.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-
-        // Create Recents on secondary display.
-        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
-                ActivityDisplay.POSITION_TOP);
-        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        new ActivityBuilder(mService).setTask(task).build();
-
-        final String reason = "findTaskToMoveToFront";
-        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
-                false);
-
-        verify(display, never()).moveHomeStackToFront(contains(reason));
-    }
-
-    /**
-     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
-     * the stack is the top focused.
-     */
-    @Test
-    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
-        // Create a stack at bottom.
-        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
-        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, false /* onTop */));
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
-        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        display.positionChildAtBottom(targetStack);
-
-        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
-        // is the current top focused stack.
-        assertFalse(targetStack.isTopStackOnDisplay());
-        doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
-
-        // Use the stack as target to resume.
-        mSupervisor.resumeFocusedStacksTopActivitiesLocked(
-                targetStack, activity, null /* targetOptions */);
-
-        // Verify the target stack should resume its activity.
-        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
-                eq(activity), eq(null /* targetOptions */));
-    }
-
-    /**
-     * Tests home activities that targeted sdk before Q cannot start on secondary display.
-     */
-    @Test
-    public void testStartHomeTargetSdkBeforeQ() throws Exception {
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        final ActivityInfo info = new ActivityInfo();
-        info.launchMode = LAUNCH_MULTIPLE;
-        info.applicationInfo = new ApplicationInfo();
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-
-        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
-        assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
-                false /* allowInstrumenting */));
-    }
-
-    /**
-     * Tests that home activities can be started on the displays that supports system decorations.
-     */
-    @Test
-    public void testStartHomeOnAllDisplays() {
-        // Create secondary displays.
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondDisplay, POSITION_TOP);
-        doReturn(true).when(secondDisplay).supportsSystemDecorations();
-
-        // Create mock tasks and other necessary mocks.
-        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
-        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doAnswer(i -> taskBuilder.build()).when(factory)
-                .create(any(), anyInt(), any(), any(), any(), any());
-        doReturn(true).when(mService.mStackSupervisor)
-                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
-        doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean());
-
-        mSupervisor.startHomeOnAllDisplays(0, "testStartHome");
-
-        assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome());
-        assertNotNull(secondDisplay.getTopStack());
-        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
-    }
-
-    /**
-     * Tests that home activities won't be started before booting when display added.
-     */
-    @Test
-    public void testNotStartHomeBeforeBoot() {
-        final int displayId = 1;
-        final boolean isBooting = mService.mAmInternal.isBooting();
-        final boolean isBooted = mService.mAmInternal.isBooted();
-        try {
-            mService.mAmInternal.setBooting(false);
-            mService.mAmInternal.setBooted(false);
-            mSupervisor.onDisplayAdded(displayId);
-            verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
-        } finally {
-            mService.mAmInternal.setBooting(isBooting);
-            mService.mAmInternal.setBooted(isBooted);
-        }
-    }
-
-    /**
-     * Tests whether home can be started if being instrumented.
-     */
-    @Test
-    public void testCanStartHomeWhenInstrumented() {
-        final ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        final WindowProcessController app = mock(WindowProcessController.class);
-        doReturn(app).when(mService).getProcessController(any(), anyInt());
-
-        // Can not start home if we don't want to start home while home is being instrumented.
-        doReturn(true).when(app).isInstrumenting();
-        assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                false /* allowInstrumenting*/));
-
-        // Can start home for other cases.
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                true /* allowInstrumenting*/));
-
-        doReturn(false).when(app).isInstrumenting();
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                false /* allowInstrumenting*/));
-        assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
-                true /* allowInstrumenting*/));
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 2fe45b8..048384e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -74,7 +74,7 @@
     @Before
     public void setUp() throws Exception {
         setupActivityTaskManagerService();
-        mDefaultDisplay = mSupervisor.getDefaultDisplay();
+        mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
         mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */));
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
@@ -112,7 +112,7 @@
         r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
         assertEquals(r, mStack.getResumedActivity());
 
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
@@ -130,7 +130,7 @@
         r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
         assertEquals(r, mStack.getResumedActivity());
 
-        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
 
@@ -233,14 +233,14 @@
                 .setStack(mStack)
                 .setUid(0)
                 .build();
-        final TaskRecord task = r.getTask();
+        final TaskRecord task = r.getTaskRecord();
         // Overlay must be for a different user to prevent recognizing a matching top activity
         final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
                 .setUid(UserHandle.PER_USER_RANGE * 2).build();
         taskOverlay.mTaskOverlay = true;
 
-        final ActivityStackSupervisor.FindTaskResult result =
-                new ActivityStackSupervisor.FindTaskResult();
+        final RootActivityContainer.FindTaskResult result =
+                new RootActivityContainer.FindTaskResult();
         mStack.findTaskLocked(r, result);
 
         assertEquals(r, task.getTopActivity(false /* includeOverlays */));
@@ -700,7 +700,7 @@
         // should be destroyed immediately with updating configuration to restore original state.
         final ActivityRecord activity1 = finishCurrentActivity(stack1);
         assertEquals(DESTROYING, activity1.getState());
-        verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */,
+        verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
                 eq(display.mDisplayId), anyBoolean(), anyBoolean());
     }
 
@@ -778,7 +778,7 @@
         final ActivityDisplay display = mock(ActivityDisplay.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
 
-        doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
+        doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt());
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
         doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index 9d93c85..2ba2fdb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -74,7 +74,7 @@
         final ActivityRecord activity = new ActivityBuilder(mService).build();
         final ActivityRecord source = new ActivityBuilder(mService).build();
         final int startFlags = random.nextInt();
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final WindowProcessController wpc = new WindowProcessController(mService,
                 mService.mContext.getApplicationInfo(), "name", 12345,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 27fa20b..350114c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -90,6 +90,8 @@
     @Mock
     private ActivityTaskManagerService mService;
     @Mock
+    private RootActivityContainer mRootActivityContainer;
+    @Mock
     private ActivityStackSupervisor mSupervisor;
     @Mock
     private DevicePolicyManagerInternal mDevicePolicyManager;
@@ -111,7 +113,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mService.mAmInternal = mAmInternal;
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext);
+        mInterceptor = new ActivityStartInterceptor(
+                mService, mSupervisor, mRootActivityContainer, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
                 TEST_START_FLAGS, TEST_CALLING_PACKAGE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 50aa541..5c918b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
 import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
@@ -69,6 +70,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
@@ -110,6 +112,8 @@
     private static final int FAKE_CALLING_UID = 666;
     private static final int FAKE_REAL_CALLING_UID = 667;
     private static final String FAKE_CALLING_PACKAGE = "com.whatever.dude";
+    private static final int UNIMPORTANT_UID = 12345;
+    private static final int UNIMPORTANT_UID2 = 12346;
 
     @Before
     public void setUp() throws Exception {
@@ -125,7 +129,7 @@
     public void testUpdateLaunchBounds() {
         // When in a non-resizeable stack, the task bounds should be updated.
         final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
         final Rect bounds = new Rect(10, 10, 100, 100);
@@ -136,7 +140,7 @@
 
         // When in a resizeable stack, the stack bounds should be updated as well.
         final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
-                .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack(
+                .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */))
                 .build();
         assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class);
@@ -240,7 +244,7 @@
         final ActivityRecord source = builder.build();
 
         if (!containsConditions(preconditions, PRECONDITION_NO_INTENT_COMPONENT)) {
-            intent.setComponent(source.realActivity);
+            intent.setComponent(source.mActivityComponent);
         }
 
         if (containsConditions(preconditions, PRECONDITION_DISALLOW_APP_SWITCHING)) {
@@ -259,11 +263,11 @@
                     PRECONDITION_ACTIVITY_SUPPORTS_INTENT_EXCEPTION)) {
                 doAnswer((inv) -> {
                     throw new RemoteException();
-                }).when(packageManager).activitySupportsIntent(eq(source.realActivity), eq(intent),
-                        any());
+                }).when(packageManager).activitySupportsIntent(
+                        eq(source.mActivityComponent), eq(intent), any());
             } else {
                 doReturn(!containsConditions(preconditions, PRECONDITION_NO_VOICE_SESSION_SUPPORT))
-                        .when(packageManager).activitySupportsIntent(eq(source.realActivity),
+                        .when(packageManager).activitySupportsIntent(eq(source.mActivityComponent),
                         eq(intent), any());
             }
         } catch (RemoteException e) {
@@ -314,7 +318,7 @@
      * Creates a {@link ActivityStarter} with default parameters and necessary mocks.
      *
      * @param launchFlags The intent flags to launch activity.
-     * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for
+     * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for
      *                           always launching to the testing stack. Set to false when allowing
      *                           the activity can be launched to any stack that is decided by real
      *                           implementation.
@@ -323,14 +327,14 @@
     private ActivityStarter prepareStarter(@Intent.Flags int launchFlags,
             boolean mockGetLaunchStack) {
         // always allow test to start activity.
-        doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission(
+        doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
         // instrument the stack and task used.
-        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+        final TaskRecord task = new TaskBuilder(mSupervisor)
                 .setCreateStack(false)
                 .build();
 
@@ -343,9 +347,9 @@
 
         if (mockGetLaunchStack) {
             // Direct starter to use spy stack.
-            doReturn(stack).when(mService.mStackSupervisor)
+            doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean());
-            doReturn(stack).when(mService.mStackSupervisor)
+            doReturn(stack).when(mRootActivityContainer)
                     .getLaunchStack(any(), any(), any(), anyBoolean(), any());
         }
 
@@ -427,7 +431,7 @@
                 .setCreateTask(true)
                 .build();
 
-        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
 
         final ActivityRecord reusableActivity = new ActivityBuilder(mService)
                 .setCreateTask(true)
@@ -435,13 +439,13 @@
 
         // Create reusable activity after entering split-screen so that it is the top secondary
         // stack.
-        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         // Set focus back to primary.
-        final ActivityStack focusStack = focusActivity.getStack();
+        final ActivityStack focusStack = focusActivity.getActivityStack();
         focusStack.moveToFront("testSplitScreenDeliverToTop");
 
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+        doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
 
         final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
 
@@ -464,16 +468,16 @@
                 .setCreateTask(true)
                 .build();
 
-        reusableActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         final ActivityRecord focusActivity = new ActivityBuilder(mService)
                 .setCreateTask(true)
                 .build();
 
         // Enter split-screen. Primary stack should have focus.
-        focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
 
-        doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt());
+        doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt());
 
         final int result = starter.setReason("testSplitScreenMoveToFront").execute();
 
@@ -486,7 +490,7 @@
      */
     @Test
     public void testTaskModeViolation() {
-        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
         ((TestActivityDisplay) display).removeAllTasks();
         assertNoTasks(display);
 
@@ -551,6 +555,123 @@
     }
 
     /**
+     * This test ensures that unsupported usecases aren't aborted when background starts are
+     * allowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsAllowed_noStartsAborted() {
+        doReturn(true).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+    }
+
+    /**
+     * This test ensures that unsupported usecases are aborted when background starts are
+     * disallowed.
+     */
+    @Test
+    public void testBackgroundActivityStartsDisallowed_unsupportedStartsAborted() {
+        doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_unsupportedUsecase_aborted", true,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+    }
+
+    /**
+     * This test ensures that supported usecases aren't aborted when background starts are
+     * disallowed.
+     * The scenarios each have only one condidion that makes them supported.
+     */
+    @Test
+    public void testBackgroundActivityStartsDisallowed_supportedStartsNotAborted() {
+        doReturn(false).when(mService).isBackgroundActivityStartsEnabled();
+
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
+                Process.ROOT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
+                Process.SYSTEM_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callingUidHasVisibleWindow_notAborted", false,
+                UNIMPORTANT_UID, true, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callingUidProcessStateTop_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_realCallingUidHasVisibleWindow_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, true, PROCESS_STATE_TOP + 1,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_realCallingUidProcessStateTop_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
+                false, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_hasForegroundActivities_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                true, false);
+        runAndVerifyBackgroundActivityStartsSubtest(
+                "disallowed_callerIsRecents_notAborted", false,
+                UNIMPORTANT_UID, false, PROCESS_STATE_TOP + 1,
+                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP + 1,
+                false, true);
+    }
+
+    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
+            int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
+            int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
+            boolean hasForegroundActivities, boolean callerIsRecents) {
+        // window visibility
+        doReturn(callingUidHasVisibleWindow).when(mService.mWindowManager).isAnyWindowVisibleForUid(
+                callingUid);
+        doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager)
+                .isAnyWindowVisibleForUid(realCallingUid);
+        // process importance
+        doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid);
+        doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid);
+        // foreground activities
+        final IApplicationThread caller = mock(IApplicationThread.class);
+        final ApplicationInfo ai = new ApplicationInfo();
+        ai.uid = callingUid;
+        final WindowProcessController callerApp =
+                new WindowProcessController(mService, ai, null, callingUid, -1, null, null);
+        callerApp.setHasForegroundActivities(hasForegroundActivities);
+        doReturn(callerApp).when(mService).getProcessController(caller);
+        // caller is recents
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        mService.mStackSupervisor.setRecentTasks(recentTasks);
+        doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+
+        final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
+                .setCaller(caller)
+                .setCallingUid(callingUid)
+                .setRealCallingUid(realCallingUid)
+                .setActivityOptions(new SafeActivityOptions(options));
+
+        final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
+
+        assertEquals(ActivityStarter.getExternalResult(
+                shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
+        verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+    }
+
+    /**
      * This test ensures that when starting an existing single task activity on secondary display
      * which is not the top focused display, it should deliver new intent to the activity and not
      * create a new stack.
@@ -562,7 +683,7 @@
 
         // Create a secondary display at bottom.
         final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM);
+        mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM);
         final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
@@ -600,7 +721,7 @@
 
         // Create a secondary display with an activity.
         final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay());
-        mSupervisor.addChild(secondaryDisplay, POSITION_TOP);
+        mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP);
         final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
                 secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
                         ACTIVITY_TYPE_STANDARD, false /* onTop */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index caabdbd..3a56419 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -34,7 +34,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
@@ -66,6 +66,8 @@
 import com.android.server.AttributeCache;
 import com.android.server.ServiceThread;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.am.PendingIntentController;
+import com.android.server.firewall.IntentFirewall;
 import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
@@ -91,6 +93,7 @@
     final TestInjector mTestInjector = new TestInjector();
 
     ActivityTaskManagerService mService;
+    RootActivityContainer mRootActivityContainer;
     ActivityStackSupervisor mSupervisor;
 
     // Default package name
@@ -115,27 +118,14 @@
     }
 
     ActivityTaskManagerService createActivityTaskManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        setupActivityManagerService(atm);
-        return atm;
+        mService = new TestActivityTaskManagerService(mContext);
+        mSupervisor = mService.mStackSupervisor;
+        mRootActivityContainer = mService.mRootActivityContainer;
+        return mService;
     }
 
     void setupActivityTaskManagerService() {
-        mService = createActivityTaskManagerService();
-        mSupervisor = mService.mStackSupervisor;
-    }
-
-    ActivityManagerService createActivityManagerService() {
-        final TestActivityTaskManagerService atm =
-                spy(new TestActivityTaskManagerService(mContext));
-        return setupActivityManagerService(atm);
-    }
-
-    ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
-        final TestActivityManagerService am = spy(new TestActivityManagerService(mTestInjector));
-        setupActivityManagerService(am, atm);
-        return am;
+        createActivityTaskManagerService();
     }
 
     /** Creates a {@link TestActivityDisplay}. */
@@ -150,36 +140,10 @@
     /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
     TestActivityDisplay addNewActivityDisplayAt(int position) {
         final TestActivityDisplay display = createNewActivityDisplay();
-        mSupervisor.addChild(display, position);
+        mRootActivityContainer.addChild(display, position);
         return display;
     }
 
-    void setupActivityManagerService(
-            TestActivityManagerService am, TestActivityTaskManagerService atm) {
-        atm.setActivityManagerService(am.mIntentFirewall, am.mPendingIntentController);
-        atm.mAmInternal = am.getLocalService();
-        am.mAtmInternal = atm.getLocalService();
-        // Makes sure the supervisor is using with the spy object.
-        atm.mStackSupervisor.setService(atm);
-        doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
-        doReturn(mock(IPackageManager.class)).when(atm).getPackageManager();
-        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
-        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-        doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        am.mActivityTaskManager = atm;
-        am.mWindowManager = prepareMockWindowManager();
-        atm.setWindowManager(am.mWindowManager);
-
-        // Put a home stack on the default display, so that we'll always have something focusable.
-        final TestActivityStackSupervisor supervisor =
-                (TestActivityStackSupervisor) atm.mStackSupervisor;
-        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-        final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
-                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
-        new ActivityBuilder(atm).setTask(task).build();
-    }
-
     /**
      * Builder for creating new activities.
      */
@@ -268,7 +232,9 @@
                     aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                     0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
                     mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
-            activity.mWindowContainerController = mock(AppWindowContainerController.class);
+            spyOn(activity);
+            activity.mAppWindowToken = mock(AppWindowToken.class);
+            doNothing().when(activity).removeWindowContainer();
 
             if (mTaskRecord != null) {
                 mTaskRecord.addActivityToTop(activity);
@@ -354,7 +320,7 @@
 
         TaskRecord build() {
             if (mStack == null && mCreateStack) {
-                mStack = mSupervisor.getDefaultDisplay().createStack(
+                mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
 
@@ -405,23 +371,71 @@
         }
     }
 
-    protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private LockTaskController mLockTaskController;
-        private ActivityTaskManagerInternal mInternal;
+    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
         private PackageManagerInternal mPmInternal;
 
         // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
         // We keep the reference in order to prevent creating it twice.
-        private ActivityStackSupervisor mTestStackSupervisor;
+        ActivityStackSupervisor mTestStackSupervisor;
+
+        ActivityDisplay mDefaultDisplay;
 
         TestActivityTaskManagerService(Context context) {
             super(context);
+            spyOn(this);
+
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
             mSupportsMultiWindow = true;
             mSupportsMultiDisplay = true;
             mSupportsSplitScreenMultiWindow = true;
             mSupportsFreeformWindowManagement = true;
             mSupportsPictureInPicture = true;
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            final TestActivityManagerService am =
+                    new TestActivityManagerService(mTestInjector, this);
+
+            spyOn(getLifecycleManager());
+            spyOn(getLockTaskController());
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            // allow background activity starts by default
+            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+        }
+
+        void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
+                ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
+            mAmInternal = amInternal;
+            initialize(intentFirewall, intentController, looper);
+            initRootActivityContainerMocks(wm);
+            setWindowManager(wm);
+        }
+
+        void initRootActivityContainerMocks(WindowManagerService wm) {
+            spyOn(mRootActivityContainer);
+            mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
+            mRootActivityContainer.mWindowManager = wm;
+            mRootActivityContainer.mDisplayManager =
+                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+            doNothing().when(mRootActivityContainer).setWindowManager(any());
+            // Invoked during {@link ActivityStack} creation.
+            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+            // Always keep things awake.
+            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+            // Called when moving activity to pinned stack.
+            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+                    anyBoolean());
+
+            // Create a default display and put a home stack on it so that we'll always have
+            // something focusable.
+            mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
+            spyOn(mDefaultDisplay);
+            mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
+            mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+            final TaskRecord task = new TaskBuilder(mStackSupervisor)
+                    .setStack(mDefaultDisplay.getHomeStack()).build();
+            new ActivityBuilder(this).setTask(task).build();
+
+            doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
         }
 
         @Override
@@ -430,54 +444,17 @@
         }
 
         @Override
-        public LockTaskController getLockTaskController() {
-            if (mLockTaskController == null) {
-                mLockTaskController = spy(super.getLockTaskController());
-            }
-
-            return mLockTaskController;
-        }
-
-        @Override
         void updateUsageStats(ActivityRecord component, boolean resumed) {
         }
 
         @Override
-        protected final ActivityStackSupervisor createStackSupervisor() {
+        protected ActivityStackSupervisor createStackSupervisor() {
             if (mTestStackSupervisor == null) {
-                final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
-                final KeyguardController keyguardController = mock(KeyguardController.class);
-
-                // Invoked during {@link ActivityStack} creation.
-                doNothing().when(supervisor).updateUIDsPresentOnDisplay();
-                // Always keep things awake.
-                doReturn(true).when(supervisor).hasAwakeDisplay();
-                // Called when moving activity to pinned stack.
-                doNothing().when(supervisor).ensureActivitiesVisibleLocked(any(), anyInt(),
-                        anyBoolean());
-                // Do not schedule idle timeouts
-                doNothing().when(supervisor).scheduleIdleTimeoutLocked(any());
-                // unit test version does not handle launch wake lock
-                doNothing().when(supervisor).acquireLaunchWakelock();
-                doReturn(keyguardController).when(supervisor).getKeyguardController();
-
-                supervisor.initialize();
-                mTestStackSupervisor = supervisor;
+                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
             }
             return mTestStackSupervisor;
         }
 
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new TestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-        ActivityTaskManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new ActivityTaskManagerService.LocalService();
-            }
-            return mInternal;
-        }
-
         @Override
         PackageManagerInternal getPackageManagerInternalLocked() {
             if (mPmInternal == null) {
@@ -524,24 +501,31 @@
         }
     }
 
+    // TODO: Replace this with a mock object since we are no longer in AMS package.
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
      */
-    static class TestActivityManagerService extends ActivityManagerService {
+    class TestActivityManagerService extends ActivityManagerService {
 
-        private ActivityManagerInternal mInternal;
-
-        TestActivityManagerService(TestInjector testInjector) {
+        TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
             super(testInjector, testInjector.mHandlerThread);
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-        }
+            spyOn(this);
 
-        ActivityManagerInternal getLocalService() {
-            if (mInternal == null) {
-                mInternal = new LocalService();
-            }
-            return mInternal;
+            mWindowManager = prepareMockWindowManager();
+            mUgmInternal = mock(UriGrantsManagerInternal.class);
+
+            atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
+                    testInjector.mHandlerThread.getLooper());
+
+            mActivityTaskManager = atm;
+            mAtmInternal = atm.mInternal;
+
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+            doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
+            doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
+            doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
         }
     }
 
@@ -549,24 +533,22 @@
      * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
      * setup not available in the test environment. Also specifies an injector for
      */
-    protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
-        private ActivityDisplay mDisplay;
+    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
         private KeyguardController mKeyguardController;
 
-        public TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
             super(service, looper);
-            mDisplayManager =
-                    (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+            spyOn(this);
             mWindowManager = prepareMockWindowManager();
             mKeyguardController = mock(KeyguardController.class);
-            setWindowContainerController(mock(RootWindowContainerController.class));
-        }
 
-        @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = spy(TestActivityDisplay.create(this, DEFAULT_DISPLAY));
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+            // Do not schedule idle timeouts
+            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            // unit test version does not handle launch wake lock
+            doNothing().when(this).acquireLaunchWakelock();
+            doReturn(mKeyguardController).when(this).getKeyguardController();
+
+            initialize();
         }
 
         @Override
@@ -575,11 +557,6 @@
         }
 
         @Override
-        ActivityDisplay getDefaultDisplay() {
-            return mDisplay;
-        }
-
-        @Override
         void setWindowManager(WindowManagerService wm) {
             mWindowManager = wm;
         }
@@ -596,7 +573,7 @@
                 DisplayInfo info) {
             if (displayId == DEFAULT_DISPLAY) {
                 return new TestActivityDisplay(supervisor,
-                        supervisor.mDisplayManager.getDisplay(displayId));
+                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
             }
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     info, DEFAULT_DISPLAY_ADJUSTMENTS);
@@ -604,7 +581,7 @@
         }
 
         TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-            super(supervisor, display);
+            super(supervisor.mService.mRootActivityContainer, display);
             // Normally this comes from display-properties as exposed by WM. Without that, just
             // hard-code to FULLSCREEN for tests.
             setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -615,7 +592,7 @@
         @Override
         <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
                 int stackId, boolean onTop) {
-            return new StackBuilder(mSupervisor).setDisplay(this)
+            return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
                     .setWindowingMode(windowingMode).setActivityType(activityType)
                     .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
         }
@@ -757,8 +734,8 @@
         }
     }
 
-    protected static class StackBuilder {
-        private final ActivityStackSupervisor mSupervisor;
+    static class StackBuilder {
+        private final RootActivityContainer mRootActivityContainer;
         private ActivityDisplay mDisplay;
         private int mStackId = -1;
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
@@ -766,9 +743,9 @@
         private boolean mOnTop = true;
         private boolean mCreateActivity = true;
 
-        StackBuilder(ActivityStackSupervisor supervisor) {
-            mSupervisor = supervisor;
-            mDisplay = mSupervisor.getDefaultDisplay();
+        StackBuilder(RootActivityContainer root) {
+            mRootActivityContainer = root;
+            mDisplay = mRootActivityContainer.getDefaultDisplay();
         }
 
         StackBuilder setWindowingMode(int windowingMode) {
@@ -805,7 +782,8 @@
         <T extends ActivityStack> T build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
+                return (T) new PinnedActivityStack(mDisplay, stackId,
+                        mRootActivityContainer.mStackSupervisor, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
@@ -821,7 +799,8 @@
                     }
                 };
             } else {
-                return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
+                return (T) new TestActivityStack(mDisplay, stackId,
+                        mRootActivityContainer.mStackSupervisor, mWindowingMode,
                         mActivityType, mOnTop, mCreateActivity);
             }
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index a907161..5556a15 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -19,12 +19,12 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.FlakyTest;
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f12619c..577859c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -24,18 +24,18 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
-import android.view.IApplicationToken;
 
 import androidx.test.filters.SmallTest;
 
@@ -111,16 +111,9 @@
         final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
 
-        // Set TestAppWindowContainerController & assign first app token state to be good to go.
-        final WindowTestUtils.TestAppWindowContainerController controller1 =
-                createAppWindowController(dc1, token1.appToken);
-        final WindowTestUtils.TestAppWindowContainerController controller2 =
-                createAppWindowController(dc1, token2.appToken);
-        controller1.setContainer(token1);
         token1.allDrawn = true;
         token1.startingDisplayed = true;
         token1.startingMoved = true;
-        controller2.setContainer(token2);
 
         // Simulate activity resume / finish flows to prepare app transition & set visibility,
         // make sure transition is set as expected for each display.
@@ -132,8 +125,8 @@
         assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition());
         // One activity window is visible for resuming & the other activity window is invisible
         // for finishing in different display.
-        controller1.setVisibility(true, false);
-        controller2.setVisibility(false, false);
+        token1.setVisibility(true, false);
+        token2.setVisibility(false, false);
 
         // Make sure each display is in animating stage.
         assertTrue(dc1.mOpeningApps.size() > 0);
@@ -174,16 +167,4 @@
         assertFalse(dc1.mOpeningApps.contains(token1));
     }
 
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            DisplayContent dc, IApplicationToken token) {
-        return createAppWindowController(
-                new WindowTestUtils.TestTaskWindowContainerController(
-                        createStackControllerOnDisplay(dc)), token);
-    }
-
-    private WindowTestUtils.TestAppWindowContainerController createAppWindowController(
-            WindowTestUtils.TestTaskWindowContainerController taskController,
-            IApplicationToken token) {
-        return new WindowTestUtils.TestAppWindowContainerController(taskController, token);
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index 4522494..dcfb879 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -20,11 +20,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.SurfaceControl.Transaction;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
 
 import android.view.SurfaceControl;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
similarity index 64%
rename from services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 552390d..0ee532d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
@@ -31,13 +32,17 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_UNSET;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -66,6 +71,8 @@
     Task mTask;
     WindowTestUtils.TestAppWindowToken mToken;
 
+    private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
+
     @Before
     public void setUp() throws Exception {
         mStack = createTaskStackOnDisplay(mDisplayContent);
@@ -112,7 +119,8 @@
         assertEquals(window1, mToken.findMainWindow());
         window1.mAnimatingExit = true;
         assertEquals(window1, mToken.findMainWindow());
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2");
+        final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken,
+                "window2");
         assertEquals(window2, mToken.findMainWindow());
         mToken.removeImmediately();
     }
@@ -150,7 +158,7 @@
         mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null,
                 mDisplayContent.getDisplayId());
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation());
-        appWindow.resizeReported = false;
+        appWindow.mResizeReported = false;
 
         // Update the orientation to perform 180 degree rotation and check that resize was reported.
         mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
@@ -158,7 +166,7 @@
                 mDisplayContent.getDisplayId());
         mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
-        assertTrue(appWindow.resizeReported);
+        assertTrue(appWindow.mResizeReported);
         appWindow.removeImmediately();
     }
 
@@ -179,11 +187,11 @@
 
         // Set initial orientation and update.
         performRotation(spiedRotation, Surface.ROTATION_90);
-        appWindow.resizeReported = false;
+        appWindow.mResizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
         performRotation(spiedRotation, Surface.ROTATION_270);
-        assertTrue(appWindow.resizeReported);
+        assertTrue(appWindow.mResizeReported);
 
         appWindow.removeImmediately();
     }
@@ -215,7 +223,8 @@
         // Can not specify orientation if app isn't visible even though it fills parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
-        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
+                mToken.getOrientation(SCREEN_ORIENTATION_BEHIND));
     }
 
     @Test
@@ -241,7 +250,8 @@
 
         // Finish relaunching and ensure flag is now not reported
         mToken.finishRelaunching();
-        assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
+        assertFalse(
+                mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow());
     }
 
     @Test
@@ -251,7 +261,7 @@
                 "closingWindow");
         closingWindow.mAnimatingExit = true;
         closingWindow.mRemoveOnExit = true;
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
 
         // We pretended that we were running an exit animation, but that should have been cleared up
@@ -261,6 +271,124 @@
     }
 
     @Test
+    public void testSetOrientation() {
+        // Assert orientation is unspecified to start.
+        assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation());
+
+        mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
+
+        mDisplayContent.removeAppToken(mToken.token);
+        // Assert orientation is unset to after container is removed.
+        assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
+
+        // Reset display frozen state
+        mWm.mDisplayFrozen = false;
+    }
+
+    @Test
+    public void testCreateRemoveStartingWindow() {
+        mToken.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertHasStartingWindow(mToken);
+        mToken.removeStartingWindow();
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(mToken);
+    }
+
+    @Test
+    public void testAddRemoveRace() {
+        // There was once a race condition between adding and removing starting windows
+        for (int i = 0; i < 1000; i++) {
+            final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken();
+
+            appToken.addStartingWindow(mPackageName,
+                    android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                    false, false);
+            appToken.removeStartingWindow();
+            waitUntilHandlersIdle();
+            assertNoStartingWindow(appToken);
+
+            appToken.getParent().getParent().removeImmediately();
+        }
+    }
+
+    @Test
+    public void testTransferStartingWindow() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        token2.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(),
+                true, true, false, true, false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    @Test
+    public void testTransferStartingWindowWhileCreating() {
+        final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken();
+        ((TestWindowManagerPolicy) token1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+                () -> {
+                    // Surprise, ...! Transfer window in the middle of the creation flow.
+                    token2.addStartingWindow(mPackageName,
+                            android.R.style.Theme, null, "Test", 0, 0, 0, 0,
+                            token1.appToken.asBinder(), true, true, false,
+                            true, false, false);
+                });
+        token1.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+        assertNoStartingWindow(token1);
+        assertHasStartingWindow(token2);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() {
+        final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(taskStack, 0 /* userId */);
+        return createTestAppWindowTokenForGivenTask(task);
+    }
+
+    private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
+        final WindowTestUtils.TestAppWindowToken appToken =
+                WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+        task.addChild(appToken, 0);
+        waitUntilHandlersIdle();
+        return appToken;
+    }
+
+    @Test
+    public void testTryTransferStartingWindowFromHiddenAboveToken() {
+        // Add two tasks on top of each other.
+        final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken();
+        final WindowTestUtils.TestAppWindowToken tokenBottom =
+                createTestAppWindowTokenForGivenTask(tokenTop.getTask());
+
+        // Add a starting window.
+        tokenTop.addStartingWindow(mPackageName,
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
+                false, false);
+        waitUntilHandlersIdle();
+
+        // Make the top one invisible, and try transferring the starting window from the top to the
+        // bottom one.
+        tokenTop.setVisibility(false, false);
+        tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded();
+
+        // Assert that the bottom window now has the starting window.
+        assertNoStartingWindow(tokenTop);
+        assertHasStartingWindow(tokenBottom);
+    }
+
+    @Test
     public void testTransitionAnimationPositionAndBounds() {
         final Rect stackBounds = new Rect(
                 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */);
@@ -285,4 +413,19 @@
         assertEquals(expectedY, outPosition.y);
         assertEquals(expectedBounds, outBounds);
     }
+
+    private void assertHasStartingWindow(AppWindowToken atoken) {
+        assertNotNull(atoken.startingSurface);
+        assertNotNull(atoken.startingData);
+        assertNotNull(atoken.startingWindow);
+    }
+
+    private void assertNoStartingWindow(AppWindowToken atoken) {
+        assertNull(atoken.startingSurface);
+        assertNull(atoken.startingWindow);
+        assertNull(atoken.startingData);
+        atoken.forAllWindows(windowState -> {
+            assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING);
+        }, true);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 991981f..ee1c8df 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -16,16 +16,17 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -145,7 +146,7 @@
     }
 
     @Test
-    public void testUpdateDimsAppliesSize() {
+    public void testUpdateDimsAppliesCrop() {
         mDimmer.dimAbove(mTransaction, 0.8f);
 
         int width = 100;
@@ -153,7 +154,7 @@
         Rect bounds = new Rect(0, 0, width, height);
         mDimmer.updateDims(mTransaction, bounds);
 
-        verify(mTransaction).setSize(getDimLayer(), width, height);
+        verify(mTransaction).setWindowCrop(getDimLayer(), width, height);
         verify(mTransaction).show(getDimLayer());
     }
 
@@ -242,13 +243,13 @@
         SurfaceControl dimLayer = getDimLayer();
         bounds.set(0, 0, 10, 10);
         mDimmer.updateDims(mTransaction, bounds);
+        verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction, times(1)).show(dimLayer);
-        verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction).setPosition(dimLayer, 0, 0);
 
         bounds.set(10, 10, 30, 30);
         mDimmer.updateDims(mTransaction, bounds);
-        verify(mTransaction).setSize(dimLayer, bounds.width(), bounds.height());
+        verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height());
         verify(mTransaction).setPosition(dimLayer, 10, 10);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 43e10f0..3b8d71d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -29,6 +29,10 @@
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
@@ -39,10 +43,6 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.annotation.SuppressLint;
 import android.content.res.Configuration;
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 18bd2e4..6767465 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -28,14 +28,11 @@
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
-import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
 public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
new file mode 100644
index 0000000..845a09f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
+import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.DisplayCutout;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.utils.WmDisplayCutout;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@Presubmit
+public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
+
+    private DisplayFrames mFrames;
+    private WindowState mWindow;
+    private int mRotation = ROTATION_0;
+    private boolean mHasDisplayCutout;
+
+    @Before
+    public void setUp() throws Exception {
+        updateDisplayFrames();
+
+        mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
+        // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
+        // changing those frames.
+        doNothing().when(mWindow).computeFrameLw();
+
+        final WindowManager.LayoutParams attrs = mWindow.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        attrs.format = PixelFormat.TRANSLUCENT;
+    }
+
+    public void setRotation(int rotation) {
+        mRotation = rotation;
+        updateDisplayFrames();
+    }
+
+    public void addDisplayCutout() {
+        mHasDisplayCutout = true;
+        updateDisplayFrames();
+    }
+
+    private void updateDisplayFrames() {
+        final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
+                mHasDisplayCutout);
+        mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
+    }
+
+    @Test
+    public void addingWindow_doesNotTamperWithSysuiFlags() {
+        mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        assertEquals(0, mWindow.mAttrs.systemUiVisibility);
+        assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
+    }
+
+    @Test
+    public void layoutWindowLw_appDrawsBars() {
+        synchronized (mWm.mGlobalLock) {
+            mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars() {
+        synchronized (mWm.mGlobalLock) {
+            mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception {
+        synchronized (mWm.mGlobalLock) {
+            mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+            mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_never() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
+            mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+        }
+    }
+
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_landscape() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+            setRotation(ROTATION_90);
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+            assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getContentFrameLw(),
+                    DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+            assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_seascape() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+            setRotation(ROTATION_270);
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+            assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+            assertInsetBy(mWindow.getContentFrameLw(),
+                    NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
+            assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+            assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+            setRotation(ROTATION_90);
+
+            mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+            assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getContentFrameLw(),
+                    DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+
+            mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
+            mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
+            mWindow.mAttrs.width = DISPLAY_WIDTH;
+            mWindow.mAttrs.height = DISPLAY_HEIGHT;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
+            assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        }
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
+        synchronized (mWm.mGlobalLock) {
+            addDisplayCutout();
+            setRotation(ROTATION_90);
+
+            mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+            mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            addWindow(mWindow);
+
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+            mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+            assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+            assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getContentFrameLw(),
+                    DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+            assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        }
+    }
+
+    @Test
+    public void layoutHint_appWindow() {
+        synchronized (mWm.mGlobalLock) {
+            // Initialize DisplayFrames
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+            final Rect outFrame = new Rect();
+            final Rect outContentInsets = new Rect();
+            final Rect outStableInsets = new Rect();
+            final Rect outOutsets = new Rect();
+            final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                    new DisplayCutout.ParcelableWrapper();
+
+            mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames,
+                    false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+                    outOutsets, outDisplayCutout);
+
+            assertThat(outFrame, is(mFrames.mUnrestricted));
+            assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+            assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT)));
+            assertThat(outOutsets, is(new Rect()));
+            assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        }
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask() {
+        synchronized (mWm.mGlobalLock) {
+            // Initialize DisplayFrames
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+            final Rect taskBounds = new Rect(100, 100, 200, 200);
+
+            final Rect outFrame = new Rect();
+            final Rect outContentInsets = new Rect();
+            final Rect outStableInsets = new Rect();
+            final Rect outOutsets = new Rect();
+            final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                    new DisplayCutout.ParcelableWrapper();
+
+            mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+                    false /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+                    outOutsets, outDisplayCutout);
+
+            assertThat(outFrame, is(taskBounds));
+            assertThat(outContentInsets, is(new Rect()));
+            assertThat(outStableInsets, is(new Rect()));
+            assertThat(outOutsets, is(new Rect()));
+            assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        }
+    }
+
+    @Test
+    public void layoutHint_appWindowInTask_outsideContentFrame() {
+        synchronized (mWm.mGlobalLock) {
+            // Initialize DisplayFrames
+            mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+            // Task is in the nav bar area (usually does not happen, but this is similar enough to
+            // the possible overlap with the IME)
+            final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1,
+                    200, mFrames.mContent.bottom + 10);
+
+            final Rect outFrame = new Rect();
+            final Rect outContentInsets = new Rect();
+            final Rect outStableInsets = new Rect();
+            final Rect outOutsets = new Rect();
+            final DisplayCutout.ParcelableWrapper outDisplayCutout =
+                    new DisplayCutout.ParcelableWrapper();
+
+            mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames,
+                    true /* floatingStack */, outFrame, outContentInsets, outStableInsets,
+                    outOutsets, outDisplayCutout);
+
+            assertThat(outFrame, is(taskBounds));
+            assertThat(outContentInsets, is(new Rect()));
+            assertThat(outStableInsets, is(new Rect()));
+            assertThat(outOutsets, is(new Rect()));
+            assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
+        }
+    }
+
+    /**
+     * Asserts that {@code actual} is inset by the given amounts from the full display rect.
+     *
+     * Convenience wrapper for when only the top and bottom inset are non-zero.
+     */
+    private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
+            int expectedInsetBottom) {
+        assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
+    }
+
+    /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
+    private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
+            int expectedInsetRight, int expectedInsetBottom) {
+        assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
+                mFrames.mDisplayWidth - expectedInsetRight,
+                mFrames.mDisplayHeight - expectedInsetBottom), actual);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 07d5fea..8349ac7f 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -42,12 +42,9 @@
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
 public class DisplayPolicyTests extends WindowTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
new file mode 100644
index 0000000..e988994
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atMost;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManagerInternal;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+import android.view.Surface;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class for {@link DisplayRotation}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DisplayRotationTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class DisplayRotationTests {
+    private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
+
+    private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
+
+    private WindowManagerService mMockWm;
+    private DisplayContent mMockDisplayContent;
+    private DisplayPolicy mMockDisplayPolicy;
+    private Context mMockContext;
+    private Resources mMockRes;
+    private SensorManager mMockSensorManager;
+    private Sensor mFakeSensor;
+    private DisplayWindowSettings mMockDisplayWindowSettings;
+    private ContentResolver mMockResolver;
+    private FakeSettingsProvider mFakeSettingsProvider;
+    private StatusBarManagerInternal mMockStatusBarManagerInternal;
+
+    // Fields below are callbacks captured from test target.
+    private ContentObserver mShowRotationSuggestionsObserver;
+    private ContentObserver mAccelerometerRotationObserver;
+    private ContentObserver mUserRotationObserver;
+    private SensorEventListener mOrientationSensorListener;
+
+    private DisplayRotationBuilder mBuilder;
+
+    private DisplayRotation mTarget;
+
+    @Before
+    public void setUp() {
+        FakeSettingsProvider.clearSettingsProvider();
+
+        mMockWm = mock(WindowManagerService.class);
+        mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+
+        mPreviousStatusBarManagerInternal = LocalServices.getService(
+                StatusBarManagerInternal.class);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        mMockStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+
+        mBuilder = new DisplayRotationBuilder();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        if (mPreviousStatusBarManagerInternal != null) {
+            LocalServices.addService(StatusBarManagerInternal.class,
+                    mPreviousStatusBarManagerInternal);
+            mPreviousStatusBarManagerInternal = null;
+        }
+    }
+
+    // ================================
+    // Display Settings Related Tests
+    // ================================
+    @Test
+    public void testLocksUserRotation_LockRotation_DefaultDisplay() throws Exception {
+        mBuilder.build();
+
+        freezeRotation(Surface.ROTATION_180);
+
+        assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+        assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+        assertEquals(0, Settings.System.getInt(mMockResolver,
+                Settings.System.ACCELEROMETER_ROTATION));
+        assertEquals(Surface.ROTATION_180, Settings.System.getInt(mMockResolver,
+                Settings.System.USER_ROTATION));
+    }
+
+    @Test
+    public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception {
+        mBuilder.mIsDefaultDisplay = false;
+
+        mBuilder.build();
+
+        freezeRotation(Surface.ROTATION_180);
+
+        assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, mTarget.getUserRotationMode());
+        assertEquals(Surface.ROTATION_180, mTarget.getUserRotation());
+
+        verify(mMockDisplayWindowSettings).setUserRotation(mMockDisplayContent,
+                WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_180);
+    }
+
+    @Test
+    public void testPersistUserRotation_UnlockRotation_DefaultDisplay() throws Exception {
+        mBuilder.build();
+
+        thawRotation();
+
+        assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+        assertEquals(1, Settings.System.getInt(mMockResolver,
+                Settings.System.ACCELEROMETER_ROTATION));
+    }
+
+    @Test
+    public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception {
+        mBuilder.mIsDefaultDisplay = false;
+
+        mBuilder.build();
+
+        thawRotation();
+
+        assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, mTarget.getUserRotationMode());
+
+        verify(mMockDisplayWindowSettings).setUserRotation(same(mMockDisplayContent),
+                eq(WindowManagerPolicy.USER_ROTATION_FREE), anyInt());
+    }
+
+    @Test
+    public void testPersistsFixedToUserRotation() throws Exception {
+        mBuilder.build();
+
+        mTarget.setFixedToUserRotation(true);
+
+        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true);
+
+        reset(mMockDisplayWindowSettings);
+        mTarget.setFixedToUserRotation(false);
+
+        verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false);
+    }
+
+    // ========================================
+    // Tests for User Rotation based Rotation
+    // ========================================
+    @Test
+    public void testReturnsUserRotation_UserRotationLocked_NoAppRequest()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        freezeRotation(Surface.ROTATION_180);
+
+        assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+    }
+
+    @Test
+    public void testReturnsUserRotation_UserRotationLocked_CompatibleAppRequest()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        freezeRotation(Surface.ROTATION_180);
+
+        assertEquals(Surface.ROTATION_180, mTarget.rotationForOrientation(
+                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90));
+    }
+
+    @Test
+    public void testReturnsSidesays_UserRotationLocked_IncompatibleAppRequest()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        freezeRotation(Surface.ROTATION_180);
+
+        final int rotation = mTarget.rotationForOrientation(
+                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_90);
+        assertTrue("Rotation should be sideways, but it's "
+                        + Surface.rotationToString(rotation),
+                rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+    }
+
+    // =================================
+    // Tests for Sensor based Rotation
+    // =================================
+    private void verifyOrientationListenerRegistration(int numOfInvocation) {
+        final ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+                SensorEventListener.class);
+        verify(mMockSensorManager, times(numOfInvocation)).registerListener(
+                listenerCaptor.capture(),
+                same(mFakeSensor),
+                anyInt(),
+                any());
+        if (numOfInvocation > 0) {
+            mOrientationSensorListener = listenerCaptor.getValue();
+        }
+    }
+
+    @Test
+    public void testNotEnablesSensor_AutoRotationNotSupported() throws Exception {
+        mBuilder.setSupportAutoRotation(false).build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_ScreenNotOn() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(false);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_NotAwake() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(false);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_KeyguardNotDrawnCompletely() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(false);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_WindowManagerNotDrawnCompletely() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(false);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_FixedUserRotation() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.setFixedToUserRotation(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_ForceDefaultRotation() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    @Test
+    public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(0);
+    }
+
+    private void enableOrientationSensor() {
+        when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true);
+        when(mMockDisplayPolicy.isAwake()).thenReturn(true);
+        when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true);
+        when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true);
+        mTarget.updateOrientationListener();
+        verifyOrientationListenerRegistration(1);
+    }
+
+    private SensorEvent createSensorEvent(int rotation) throws Exception {
+        final Constructor<SensorEvent> constructor =
+                SensorEvent.class.getDeclaredConstructor(int.class);
+        constructor.setAccessible(true);
+        final SensorEvent event = constructor.newInstance(1);
+        event.sensor = mFakeSensor;
+        event.values[0] = rotation;
+        event.timestamp = SystemClock.elapsedRealtimeNanos();
+        return event;
+    }
+
+    @Test
+    public void testReturnsSensorRotation_RotationThawed() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+
+        assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+    }
+
+    private boolean waitForUiHandler() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        UiThread.getHandler().post(latch::countDown);
+        return latch.await(UI_HANDLER_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void testUpdatesRotationWhenSensorUpdates_RotationThawed() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+        assertTrue(waitForUiHandler());
+
+        verify(mMockWm).updateRotation(false, false);
+    }
+
+    @Test
+    public void testNotifiesChoiceWhenSensorUpdates_RotationLocked() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        freezeRotation(Surface.ROTATION_270);
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
+        assertTrue(waitForUiHandler());
+
+        verify(mMockStatusBarManagerInternal).onProposedRotationChanged(Surface.ROTATION_90, true);
+    }
+
+    @Test
+    public void testReturnsCompatibleRotation_SensorEnabled_RotationThawed() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        thawRotation();
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+        final int rotation = mTarget.rotationForOrientation(SCREEN_ORIENTATION_LANDSCAPE,
+                Surface.ROTATION_0);
+        assertTrue("Rotation should be sideways but it's "
+                + Surface.rotationToString(rotation),
+                rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+    }
+
+    @Test
+    public void testReturnsUserRotation_SensorEnabled_RotationLocked() throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        freezeRotation(Surface.ROTATION_270);
+
+        enableOrientationSensor();
+
+        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_180));
+
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+    }
+
+    // =================================
+    // Tests for Policy based Rotation
+    // =================================
+    @Test
+    public void testReturnsUserRotation_ForceDefaultRotation() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+                Surface.ROTATION_180));
+    }
+
+    @Test
+    public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false);
+
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+                Surface.ROTATION_180));
+    }
+
+    @Test
+    public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception {
+        mBuilder.build();
+        when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation))
+                .thenReturn(true);
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true);
+
+        assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT,
+                Surface.ROTATION_180));
+    }
+
+    @Test
+    public void testReturnsLidOpenRotation_LidOpen() throws Exception {
+        mBuilder.setLidOpenRotation(Surface.ROTATION_90).build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        when(mMockDisplayPolicy.getLidState()).thenReturn(
+                WindowManagerPolicy.WindowManagerFuncs.LID_OPEN);
+
+        freezeRotation(Surface.ROTATION_270);
+
+        assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+    }
+
+    @Test
+    public void testReturnsCarDockRotation_CarDockedMode() throws Exception {
+        mBuilder.setCarDockRotation(Surface.ROTATION_270).build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_CAR);
+
+        freezeRotation(Surface.ROTATION_90);
+
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+    }
+
+    @Test
+    public void testReturnsDeskDockRotation_DeskDockedMode() throws Exception {
+        mBuilder.setDeskDockRotation(Surface.ROTATION_270).build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        when(mMockDisplayPolicy.getDockMode()).thenReturn(Intent.EXTRA_DOCK_STATE_DESK);
+
+        freezeRotation(Surface.ROTATION_90);
+
+        assertEquals(Surface.ROTATION_270, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_90));
+    }
+
+    @Test
+    public void testReturnsUserRotation_FixedToUserRotation_IgnoreIncompatibleAppRequest()
+            throws Exception {
+        mBuilder.build();
+        configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
+
+        mTarget.setFixedToUserRotation(true);
+
+        freezeRotation(Surface.ROTATION_180);
+
+        final int rotation = mTarget.rotationForOrientation(
+                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, Surface.ROTATION_90);
+        assertEquals(Surface.ROTATION_180, rotation);
+    }
+
+    @Test
+    public void testReturnsUserRotation_NonDefaultDisplay() throws Exception {
+        mBuilder.setIsDefaultDisplay(false).build();
+        configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false);
+
+        freezeRotation(Surface.ROTATION_90);
+
+        assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
+                SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
+    }
+
+    /**
+     * Call {@link DisplayRotation#configure(int, int, int, int)} to configure {@link #mTarget}
+     * according to given parameters.
+     */
+    private void configureDisplayRotation(int displayOrientation, boolean isCar, boolean isTv) {
+        final int width;
+        final int height;
+        switch (displayOrientation) {
+            case SCREEN_ORIENTATION_LANDSCAPE:
+                width = 1920;
+                height = 1080;
+                break;
+            case SCREEN_ORIENTATION_PORTRAIT:
+                width = 1080;
+                height = 1920;
+                break;
+            default:
+                throw new IllegalArgumentException("displayOrientation needs to be either landscape"
+                        + " or portrait, but we got "
+                        + ActivityInfo.screenOrientationToString(displayOrientation));
+        }
+
+        final PackageManager mockPackageManager = mock(PackageManager.class);
+        when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
+        when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
+                .thenReturn(isCar);
+        when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
+                .thenReturn(isTv);
+
+        final int shortSizeDp = (isCar || isTv) ? 540 : 720;
+        final int longSizeDp = 960;
+        mTarget.configure(width, height, shortSizeDp, longSizeDp);
+    }
+
+    private void freezeRotation(int rotation) {
+        mTarget.freezeRotation(rotation);
+
+        if (mTarget.isDefaultDisplay) {
+            mAccelerometerRotationObserver.onChange(false);
+            mUserRotationObserver.onChange(false);
+        }
+    }
+
+    private void thawRotation() {
+        mTarget.thawRotation();
+
+        if (mTarget.isDefaultDisplay) {
+            mAccelerometerRotationObserver.onChange(false);
+            mUserRotationObserver.onChange(false);
+        }
+    }
+
+    private class DisplayRotationBuilder {
+        private boolean mIsDefaultDisplay = true;
+        private boolean mSupportAutoRotation = true;
+
+        private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+        private int mCarDockRotation;
+        private int mDeskDockRotation;
+        private int mUndockedHdmiRotation;
+
+        private DisplayRotationBuilder setIsDefaultDisplay(boolean isDefaultDisplay) {
+            mIsDefaultDisplay = isDefaultDisplay;
+            return this;
+        }
+
+        private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
+            mSupportAutoRotation = supportAutoRotation;
+            return this;
+        }
+
+        private DisplayRotationBuilder setLidOpenRotation(int rotation) {
+            mLidOpenRotation = rotation;
+            return this;
+        }
+
+        private DisplayRotationBuilder setCarDockRotation(int rotation) {
+            mCarDockRotation = rotation;
+            return this;
+        }
+
+        private DisplayRotationBuilder setDeskDockRotation(int rotation) {
+            mDeskDockRotation = rotation;
+            return this;
+        }
+
+        private DisplayRotationBuilder setUndockedHdmiRotation(int rotation) {
+            mUndockedHdmiRotation = rotation;
+            return this;
+        }
+
+        private void captureObservers() {
+            ArgumentCaptor<ContentObserver> captor = ArgumentCaptor.forClass(
+                    ContentObserver.class);
+            verify(mMockResolver, atMost(1)).registerContentObserver(
+                    eq(Settings.Secure.getUriFor(Settings.Secure.SHOW_ROTATION_SUGGESTIONS)),
+                    anyBoolean(),
+                    captor.capture(),
+                    anyInt());
+            if (!captor.getAllValues().isEmpty()) {
+                mShowRotationSuggestionsObserver = captor.getValue();
+            }
+
+            captor = ArgumentCaptor.forClass(ContentObserver.class);
+            verify(mMockResolver, atMost(1)).registerContentObserver(
+                    eq(Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION)),
+                    anyBoolean(),
+                    captor.capture(),
+                    anyInt());
+            if (!captor.getAllValues().isEmpty()) {
+                mAccelerometerRotationObserver = captor.getValue();
+            }
+
+            captor = ArgumentCaptor.forClass(ContentObserver.class);
+            verify(mMockResolver, atMost(1)).registerContentObserver(
+                    eq(Settings.System.getUriFor(Settings.System.USER_ROTATION)),
+                    anyBoolean(),
+                    captor.capture(),
+                    anyInt());
+            if (!captor.getAllValues().isEmpty()) {
+                mUserRotationObserver = captor.getValue();
+            }
+        }
+
+        private Sensor createSensor(int type) throws Exception {
+            Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+            constr.setAccessible(true);
+            Sensor sensor = constr.newInstance();
+
+            setSensorType(sensor, type);
+            setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type);
+            setSensorField(sensor, "mVendor", "Mock Vendor");
+            setSensorField(sensor, "mVersion", 1);
+            setSensorField(sensor, "mHandle", -1);
+            setSensorField(sensor, "mMaxRange", 10);
+            setSensorField(sensor, "mResolution", 1);
+            setSensorField(sensor, "mPower", 1);
+            setSensorField(sensor, "mMinDelay", 1000);
+            setSensorField(sensor, "mMaxDelay", 1000000000);
+            setSensorField(sensor, "mFlags", 0);
+            setSensorField(sensor, "mId", -1);
+
+            return sensor;
+        }
+
+        private void setSensorType(Sensor sensor, int type) throws Exception {
+            Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+            setter.setAccessible(true);
+            setter.invoke(sensor, type);
+        }
+
+        private void setSensorField(Sensor sensor, String fieldName, Object value)
+                throws Exception {
+            Field field = Sensor.class.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            field.set(sensor, value);
+        }
+
+        private int convertRotationToDegrees(@Surface.Rotation int rotation) {
+            switch (rotation) {
+                case Surface.ROTATION_0:
+                    return 0;
+                case Surface.ROTATION_90:
+                    return 90;
+                case Surface.ROTATION_180:
+                    return 180;
+                case Surface.ROTATION_270:
+                    return 270;
+                default:
+                    return -1;
+            }
+        }
+
+        private void build() throws Exception {
+            mMockContext = mock(Context.class);
+
+            mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class);
+            mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay;
+
+            mMockDisplayPolicy = mock(DisplayPolicy.class);
+
+            mMockRes = mock(Resources.class);
+            when(mMockContext.getResources()).thenReturn((mMockRes));
+            when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
+                    .thenReturn(mSupportAutoRotation);
+            when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
+                    .thenReturn(convertRotationToDegrees(mLidOpenRotation));
+            when(mMockRes.getInteger(com.android.internal.R.integer.config_carDockRotation))
+                    .thenReturn(convertRotationToDegrees(mCarDockRotation));
+            when(mMockRes.getInteger(com.android.internal.R.integer.config_deskDockRotation))
+                    .thenReturn(convertRotationToDegrees(mDeskDockRotation));
+            when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation))
+                    .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
+
+            mMockSensorManager = mock(SensorManager.class);
+            when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
+                    .thenReturn(mMockSensorManager);
+            mFakeSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
+            when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
+                    Collections.singletonList(mFakeSensor));
+
+            mMockResolver = mock(ContentResolver.class);
+            when(mMockContext.getContentResolver()).thenReturn(mMockResolver);
+            mFakeSettingsProvider = new FakeSettingsProvider();
+            when(mMockResolver.acquireProvider(Settings.AUTHORITY))
+                    .thenReturn(mFakeSettingsProvider.getIContentProvider());
+
+            mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
+            mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+                    mMockDisplayWindowSettings, mMockContext, new Object());
+
+            captureObservers();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index b823e70..8e881b5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -21,13 +21,19 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Matchers.eq;
 
 import android.app.WindowConfiguration;
 import android.platform.test.annotations.Presubmit;
@@ -378,6 +384,33 @@
                 mSecondaryDisplay.getDisplayRotation().getUserRotation());
     }
 
+    @Test
+    public void testNotFixedToUserRotationByDefault() {
+        mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED,
+                Surface.ROTATION_0);
+
+        final DisplayRotation displayRotation = mock(DisplayRotation.class);
+        mPrimaryDisplay = spy(mPrimaryDisplay);
+        when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false));
+    }
+
+    @Test
+    public void testSetFixedToUserRotation() {
+        mTarget.setFixedToUserRotation(mPrimaryDisplay, true);
+
+        final DisplayRotation displayRotation = mock(DisplayRotation.class);
+        mPrimaryDisplay = spy(mPrimaryDisplay);
+        when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation);
+
+        applySettingsToDisplayByNewInstance(mPrimaryDisplay);
+
+        verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true));
+    }
+
     private static void assertOverscan(DisplayContent display, int left, int top, int right,
             int bottom) {
         final DisplayInfo info = display.getDisplayInfo();
diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
index a04bf16..3206208 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java
@@ -30,12 +30,9 @@
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
-@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
 public class DockedStackDividerControllerTests {
diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 55e766d..f1c6eab 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -20,13 +20,14 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
 
 import android.content.ClipData;
 import android.graphics.PixelFormat;
@@ -171,7 +172,7 @@
         try {
             final SurfaceControl surface = new SurfaceControl.Builder(appSession)
                     .setName("drag surface")
-                    .setSize(100, 100)
+                    .setBufferSize(100, 100)
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
new file mode 100644
index 0000000..88215449
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsSourceProviderTest extends WindowTestsBase {
+
+    private InsetsSource mSource = new InsetsSource(TYPE_TOP_BAR);
+    private InsetsSourceProvider mProvider;
+
+    @Before
+    public void setUp() throws Exception {
+        mSource.setVisible(true);
+        mProvider = new InsetsSourceProvider(mSource,
+                mDisplayContent.getInsetsStateController(), mDisplayContent);
+    }
+
+    @Test
+    public void testPostLayout() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        topBar.mHasSurface = true;
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0),
+                mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_invisible() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar, null);
+        mProvider.onPostLayout();
+        assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+                        false /* ignoreVisibility */));
+    }
+
+    @Test
+    public void testPostLayout_frameProvider() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        topBar.getFrameLw().set(0, 0, 500, 100);
+        mProvider.setWindow(topBar,
+                (displayFrames, windowState, rect) -> {
+                    rect.set(10, 10, 20, 20);
+                });
+        mProvider.onPostLayout();
+        assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame());
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
new file mode 100644
index 0000000..11526a8
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.view.InsetsState.TYPE_IME;
+import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
+import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@SmallTest
+@FlakyTest(detail = "Promote once confirmed non-flaky")
+@Presubmit
+public class InsetsStateControllerTest extends WindowTestsBase {
+
+    @Test
+    public void testStripForDispatch_notOwn() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+        topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+        assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
+    }
+
+    @Test
+    public void testStripForDispatch_own() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
+                .setWindow(topBar, null);
+        topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
+        assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
+    }
+
+    @Test
+    public void testStripForDispatch_navBar() {
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+        getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+        getController().getSourceProvider(TYPE_IME).setWindow(ime, null);
+        assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
+    }
+
+    @Test
+    public void testBarControllingWinChanged() {
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+        getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
+        getController().onBarControllingWindowChanged(app);
+        InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
+        assertEquals(2, controls.length);
+    }
+
+    @Test
+    public void testControlRevoked() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+        getController().onBarControllingWindowChanged(app);
+        assertNotNull(getController().getControlsForDispatch(app));
+        getController().onBarControllingWindowChanged(null);
+        assertNull(getController().getControlsForDispatch(app));
+    }
+
+    @Test
+    public void testControlRevoked_animation() {
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
+        getController().onBarControllingWindowChanged(app);
+        assertNotNull(getController().getControlsForDispatch(app));
+        topBar.cancelAnimation();
+        assertNull(getController().getControlsForDispatch(app));
+    }
+
+    private InsetsStateController getController() {
+        return mDisplayContent.getInsetsStateController();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index fa4898b..3720c85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -89,9 +89,9 @@
         final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
         final ActivityOptions options = mock(ActivityOptions.class);
 
-        mController.calculate(record.getTask(), layout, record, source, options,
+        mController.calculate(record.getTaskRecord(), layout, record, source, options,
                 new LaunchParams());
-        verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
+        verify(positioner, times(1)).onCalculate(eq(record.getTaskRecord()), eq(layout), eq(record),
                 eq(source), eq(options), any(), any());
     }
 
@@ -114,7 +114,7 @@
 
         mPersister.putLaunchParams(userId, name, expected);
 
-        mController.calculate(activity.getTask(), null /*layout*/, activity, null /*source*/,
+        mController.calculate(activity.getTaskRecord(), null /*layout*/, activity, null /*source*/,
                 null /*options*/, new LaunchParams());
         verify(positioner, times(1)).onCalculate(any(), any(), any(), any(), any(), eq(expected),
                 any());
@@ -228,7 +228,7 @@
 
         final LaunchParams result = new LaunchParams();
         final ActivityRecord vrActivity = new ActivityBuilder(mService).build();
-        vrActivity.requestedVrComponent = vrActivity.realActivity;
+        vrActivity.requestedVrComponent = vrActivity.mActivityComponent;
 
         // VR activities should always land on default display.
         mController.calculate(null /*task*/, null /*layout*/, vrActivity /*activity*/,
@@ -412,8 +412,9 @@
 
         @Override
         void getLaunchParams(TaskRecord task, ActivityRecord activity, LaunchParams params) {
-            final int userId = task != null ? task.userId : activity.userId;
-            final ComponentName name = task != null ? task.realActivity : activity.realActivity;
+            final int userId = task != null ? task.userId : activity.mUserId;
+            final ComponentName name = task != null
+                    ? task.realActivity : activity.mActivityComponent;
 
             params.reset();
             final Map<ComponentName, LaunchParams> map = mMap.get(userId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index dc22bc1..f3a125b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -110,8 +110,9 @@
         final DisplayInfo info = new DisplayInfo();
         info.uniqueId = mDisplayUniqueId;
         mTestDisplay = createNewActivityDisplay(info);
-        mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
-        when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay);
+        mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
+        when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId)))
+                .thenReturn(mTestDisplay);
 
         ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
                 ACTIVITY_TYPE_STANDARD, /* onTop */ true);
@@ -184,7 +185,7 @@
     public void testReturnsEmptyDisplayIfDisplayIsNotFound() {
         mTarget.saveTask(mTestTask);
 
-        when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
+        when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null);
 
         mTarget.getLaunchParams(mTestTask, null, mResult);
 
@@ -271,6 +272,51 @@
     }
 
     @Test
+    public void testClearsRecordInMemory() {
+        mTarget.saveTask(mTestTask);
+
+        mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+        mTarget.getLaunchParams(mTestTask, null, mResult);
+
+        assertTrue("Result should be empty.", mResult.isEmpty());
+    }
+
+    @Test
+    public void testClearsWriteQueueItem() {
+        mTarget.saveTask(mTestTask);
+
+        mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+        final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+                mUserFolderGetter);
+        target.onSystemReady();
+        target.onUnlockUser(TEST_USER_ID);
+
+        target.getLaunchParams(mTestTask, null, mResult);
+
+        assertTrue("Result should be empty.", mResult.isEmpty());
+    }
+
+    @Test
+    public void testClearsFile() {
+        mTarget.saveTask(mTestTask);
+        mPersisterQueue.flush();
+
+        mTarget.removeRecordForPackage(TEST_COMPONENT.getPackageName());
+
+        final LaunchParamsPersister target = new LaunchParamsPersister(mPersisterQueue, mSupervisor,
+                mUserFolderGetter);
+        target.onSystemReady();
+        target.onUnlockUser(TEST_USER_ID);
+
+        target.getLaunchParams(mTestTask, null, mResult);
+
+        assertTrue("Result should be empty.", mResult.isEmpty());
+    }
+
+
+    @Test
     public void testClearsRecordInMemoryOnPackageUninstalled() {
         mTarget.saveTask(mTestTask);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index 33e6063..6259fa6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -104,6 +104,7 @@
             new DexmakerShareClassLoaderRule();
 
     @Mock private ActivityStackSupervisor mSupervisor;
+    @Mock private RootActivityContainer mRootActivityContainer;
     @Mock private IDevicePolicyManager mDevicePolicyManager;
     @Mock private IStatusBarService mStatusBarService;
     @Mock private WindowManagerService mWindowManager;
@@ -129,6 +130,7 @@
         }
 
         mSupervisor.mRecentTasks = mRecentTasks;
+        mSupervisor.mRootActivityContainer = mRootActivityContainer;
 
         mLockTaskController = new LockTaskController(mContext, mSupervisor,
                 new ImmediatelyExecuteHandler());
diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 1fae317..63d9fb9 100644
--- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -18,14 +18,15 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -46,6 +47,8 @@
 @Presubmit
 public class PinnedStackControllerTest extends WindowTestsBase {
 
+    private static final int SHELF_HEIGHT = 300;
+
     @Mock private IPinnedStackListener mIPinnedStackListener;
     @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub;
 
@@ -70,8 +73,6 @@
 
         reset(mIPinnedStackListener);
 
-        final int SHELF_HEIGHT = 300;
-
         mWm.setShelfHeight(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT);
         verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false),
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 2f3f698..3c7b4b1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -110,14 +110,13 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        final TestActivityManagerService am = spy(new MyTestActivityManagerService());
-        setupActivityManagerService(am, mTestService);
+        mTestService = new MyTestActivityTaskManagerService(mContext);
         mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
         mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+        mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
+        mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+        mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -868,20 +867,20 @@
         }
 
         @Override
-        protected ActivityStackSupervisor createTestSupervisor() {
-            return new MyTestActivityStackSupervisor(this, mH.getLooper());
-        }
-
-    }
-
-    private class MyTestActivityManagerService extends TestActivityManagerService {
-        MyTestActivityManagerService() {
-            super(mTestInjector);
+        protected ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
+            }
+            return mTestStackSupervisor;
         }
 
         @Override
-        public boolean isUserRunning(int userId, int flags) {
-            return true;
+        void initRootActivityContainerMocks(WindowManagerService wm) {
+            super.initRootActivityContainerMocks(wm);
+            mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+            mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
+            mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+            mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
         }
     }
 
@@ -891,15 +890,6 @@
         }
 
         @Override
-        public void initialize() {
-            super.initialize();
-            mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
-            mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
-            addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
-            addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-        }
-
-        @Override
         RunningTasks createRunningTasks() {
             mRunningTasks = new TestRunningTasks();
             return mRunningTasks;
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index ee3bba7..cc6a58a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -21,6 +21,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
@@ -28,10 +32,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 import android.os.Binder;
 import android.os.IInterface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 50190e7..0ff67d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -25,6 +25,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
@@ -50,61 +51,50 @@
 public class RecentsAnimationTest extends ActivityTestsBase {
 
     private Context mContext = InstrumentationRegistry.getContext();
-    private TestActivityTaskManagerService mTestService;
     private ComponentName mRecentsComponent;
 
     @Before
     public void setUp() throws Exception {
         mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
-        mTestService = spy(new MyTestActivityTaskManagerService(mContext));
-        setupActivityManagerService(mTestService);
+        mService = new TestActivityTaskManagerService(mContext);
+
+        final RecentTasks recentTasks = mService.getRecentTasks();
+        spyOn(recentTasks);
+        mRecentsComponent = new ComponentName(mContext.getPackageName(), "RecentsActivity");
+        doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
     }
 
     @Test
     public void testCancelAnimationOnStackOrderChange() {
         ActivityStack fullscreenStack =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityStack recentsStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+        ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord recentsActivity = new ActivityBuilder(mService)
                 .setComponent(mRecentsComponent)
                 .setCreateTask(true)
                 .setStack(recentsStack)
                 .build();
         ActivityStack fullscreenStack2 =
-                mTestService.mStackSupervisor.getDefaultDisplay().createStack(
+                mService.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ActivityRecord fsActivity = new ActivityBuilder(mTestService)
+        ActivityRecord fsActivity = new ActivityBuilder(mService)
                 .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
                 .setCreateTask(true)
                 .setStack(fullscreenStack2)
                 .build();
-        doReturn(true).when(mTestService.mWindowManager).canStartRecentsAnimation();
+        doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         // Start the recents animation
         Intent recentsIntent = new Intent();
         recentsIntent.setComponent(mRecentsComponent);
-        mTestService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
+        mService.startRecentsActivity(recentsIntent, null, mock(IRecentsAnimationRunner.class));
 
         fullscreenStack.moveToFront("Activity start");
 
         // Ensure that the recents animation was canceled
-        verify(mTestService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
                 eq(REORDER_KEEP_IN_PLACE), any());
     }
-
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            RecentTasks recents = mock(RecentTasks.class);
-            doReturn(mRecentsComponent).when(recents).getRecentsComponent();
-            System.out.println(mRecentsComponent);
-            return recents;
-        }
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index fa53795..ad2a708 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -18,13 +18,14 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -143,8 +144,9 @@
     @Test
     public void testTimeout_scaled() throws Exception {
         mWm.setAnimationScale(2, 5.0f);
-        try{
-            final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        try {
+            final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
+                    "testWin");
             final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
                     new Point(50, 100), new Rect(50, 100, 150, 150));
             adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
@@ -163,7 +165,6 @@
         } finally {
             mWm.setAnimationScale(2, 1.0f);
         }
-
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
new file mode 100644
index 0000000..9b18388
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
+import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
+
+import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import androidx.test.filters.MediumTest;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the {@link ActivityStackSupervisor} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityStackSupervisorTests
+ */
+@MediumTest
+@Presubmit
+public class RootActivityContainerTests extends ActivityTestsBase {
+    private ActivityStack mFullscreenStack;
+
+    @Before
+    public void setUp() throws Exception {
+        setupActivityTaskManagerService();
+        mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+    }
+
+    /**
+     * This test ensures that we do not try to restore a task based off an invalid task id. We
+     * should expect {@code null} to be returned in this case.
+     */
+    @Test
+    public void testRestoringInvalidTask() {
+        ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+        TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
+                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
+        assertNull(task);
+    }
+
+    /**
+     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+     * activity stack when a new task is added.
+     */
+    @Test
+    public void testReplacingTaskInPinnedStack() {
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord firstTask = firstActivity.getTaskRecord();
+
+        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final TaskRecord secondTask = secondActivity.getTaskRecord();
+
+        mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack");
+
+        // Ensure full screen stack has both tasks.
+        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
+
+        // Move first activity to pinned stack.
+        final Rect sourceBounds = new Rect();
+        mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds,
+                0f /*aspectRatio*/, "initialMove");
+
+        final ActivityDisplay display = mFullscreenStack.getDisplay();
+        ActivityStack pinnedStack = display.getPinnedStack();
+        // Ensure a task has moved over.
+        ensureStackPlacement(pinnedStack, firstTask);
+        ensureStackPlacement(mFullscreenStack, secondTask);
+
+        // Move second activity to pinned stack.
+        mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds,
+                0f /*aspectRatio*/, "secondMove");
+
+        // Need to get stacks again as a new instance might have been created.
+        pinnedStack = display.getPinnedStack();
+        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        // Ensure stacks have swapped tasks.
+        ensureStackPlacement(pinnedStack, secondTask);
+        ensureStackPlacement(mFullscreenStack, firstTask);
+    }
+
+    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
+        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+        if (tasks == null) {
+            return;
+        }
+
+        for (TaskRecord task : tasks) {
+            assertTrue(stackTasks.contains(task));
+        }
+    }
+
+    @Test
+    public void testApplySleepTokens() {
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final KeyguardController keyguard = mSupervisor.getKeyguardController();
+        final ActivityStack stack = mock(ActivityStack.class);
+        display.addChild(stack, 0 /* position */);
+
+        // Make sure we wake and resume in the case the display is turning on and the keyguard is
+        // not showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                true /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // showing.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                true /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Make sure we wake and don't resume when the display is turning on and the keyguard is
+        // not showing as unfocused.
+        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
+                false /* displayShouldSleep */, false /* isFocusedStack */,
+                false /* keyguardShowing */, true /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+
+        // Should not do anything if the display state hasn't changed.
+        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
+                false /* displayShouldSleep */, true /* isFocusedStack */,
+                false /* keyguardShowing */, false /* expectWakeFromSleep */,
+                false /* expectResumeTopActivity */);
+    }
+
+    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
+            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
+            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
+            boolean expectResumeTopActivity) {
+        reset(stack);
+
+        doReturn(displayShouldSleep).when(display).shouldSleep();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
+
+        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
+        doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
+        mRootActivityContainer.applySleepTokens(true);
+        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
+        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
+                null /* target */, null /* targetOptions */);
+    }
+
+    /**
+     * Verifies that removal of activity with task and stack is done correctly.
+     */
+    @Test
+    public void testRemovingStackOnAppCrash() {
+        final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+        final int originalStackCount = defaultDisplay.getChildCount();
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+
+        // Let's pretend that the app has crashed.
+        firstActivity.app.setThread(null);
+        mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
+
+        // Verify that the stack was removed.
+        assertEquals(originalStackCount, defaultDisplay.getChildCount());
+    }
+
+    @Test
+    public void testFocusability() {
+        final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Under split screen primary we should be focusable when not minimized
+        mRootActivityContainer.setDockedStackMinimized(false);
+        assertTrue(stack.isFocusable());
+        assertTrue(activity.isFocusable());
+
+        // Under split screen primary we should not be focusable when minimized
+        mRootActivityContainer.setDockedStackMinimized(true);
+        assertFalse(stack.isFocusable());
+        assertFalse(activity.isFocusable());
+
+        final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(pinnedStack).build();
+
+        // We should not be focusable when in pinned mode
+        assertFalse(pinnedStack.isFocusable());
+        assertFalse(pinnedActivity.isFocusable());
+
+        // Add flag forcing focusability.
+        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+        // We should not be focusable when in pinned mode
+        assertTrue(pinnedStack.isFocusable());
+        assertTrue(pinnedActivity.isFocusable());
+
+        // Without the overridding activity, stack should not be focusable.
+        pinnedStack.removeTask(pinnedActivity.getTaskRecord(), "testFocusability",
+                REMOVE_TASK_MODE_DESTROYING);
+        assertFalse(pinnedStack.isFocusable());
+    }
+
+    /**
+     * Verify that split-screen primary stack will be chosen if activity is launched that targets
+     * split-screen secondary, but a matching existing instance is found on top of split-screen
+     * primary stack.
+     */
+    @Test
+    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() {
+        // Create primary split-screen stack with a task and an activity.
+        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
+
+        // Find a launch stack for the top activity in split-screen primary, while requesting
+        // split-screen secondary.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+        final ActivityStack result =
+                mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */);
+
+        // Assert that the primary stack is returned.
+        assertEquals(primaryStack, result);
+    }
+
+    /**
+     * Verify split-screen primary stack & task can resized by
+     * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect.
+     */
+    @Test
+    public void testResizeDockedStackForSplitScreenPrimary() {
+        final Rect taskSize = new Rect(0, 0, 600, 600);
+        final Rect stackSize = new Rect(0, 0, 300, 300);
+
+        // Create primary split-screen stack with a task.
+        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
+                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                        true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+
+        // Resize dock stack.
+        mService.resizeDockedStack(stackSize, taskSize, null, null, null);
+
+        // Verify dock stack & its task bounds if is equal as resized result.
+        assertEquals(primaryStack.getBounds(), stackSize);
+        assertEquals(task.getBounds(), taskSize);
+    }
+
+    /**
+     * Verify that home stack would be moved to front when the top activity is Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final TestActivityStack targetStack =
+                new StackBuilder(mRootActivityContainer).setOnTop(false).build();
+        final TaskRecord targetTask = targetStack.getChildAt(0);
+
+        // Create Recents on top of the display.
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType(
+                ACTIVITY_TYPE_RECENTS).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify that home stack won't be moved to front if the top activity on other display is
+     * Recents.
+     */
+    @Test
+    public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() {
+        // Create stack/task on default display.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+
+        // Create Recents on secondary display.
+        final TestActivityDisplay secondDisplay = addNewActivityDisplayAt(
+                ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_RECENTS, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
+        new ActivityBuilder(mService).setTask(task).build();
+
+        final String reason = "findTaskToMoveToFront";
+        mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
+                false);
+
+        verify(display, never()).moveHomeStackToFront(contains(reason));
+    }
+
+    /**
+     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+     * the stack is the top focused.
+     */
+    @Test
+    public void testResumeActivityWhenNonTopmostStackIsTopFocused() {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        display.positionChildAtBottom(targetStack);
+
+        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+        // is the current top focused stack.
+        assertFalse(targetStack.isTopStackOnDisplay());
+        doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack();
+
+        // Use the stack as target to resume.
+        mRootActivityContainer.resumeFocusedStacksTopActivities(
+                targetStack, activity, null /* targetOptions */);
+
+        // Verify the target stack should resume its activity.
+        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+                eq(activity), eq(null /* targetOptions */));
+    }
+
+    /**
+     * Tests home activities that targeted sdk before Q cannot start on secondary display.
+     */
+    @Test
+    public void testStartHomeTargetSdkBeforeQ() throws Exception {
+        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+        doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+        final ActivityInfo info = new ActivityInfo();
+        info.launchMode = LAUNCH_MULTIPLE;
+        info.applicationInfo = new ApplicationInfo();
+        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+                false /* allowInstrumenting */));
+
+        info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
+        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId,
+                false /* allowInstrumenting */));
+    }
+
+    /**
+     * Tests that home activities can be started on the displays that supports system decorations.
+     */
+    @Test
+    public void testStartHomeOnAllDisplays() {
+        // Create secondary displays.
+        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
+        doReturn(true).when(secondDisplay).supportsSystemDecorations();
+
+        // Create mock tasks and other necessary mocks.
+        TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false);
+        final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class);
+        TaskRecord.setTaskRecordFactory(factory);
+        doAnswer(i -> taskBuilder.build()).when(factory)
+                .create(any(), anyInt(), any(), any(), any(), any());
+        doReturn(true).when(mRootActivityContainer)
+                .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
+        doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+                any(), anyInt(), anyBoolean());
+
+        mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome");
+
+        assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome());
+        assertNotNull(secondDisplay.getTopStack());
+        assertTrue(secondDisplay.getTopStack().isActivityTypeHome());
+    }
+
+    /**
+     * Tests that home activities won't be started before booting when display added.
+     */
+    @Test
+    public void testNotStartHomeBeforeBoot() {
+        final int displayId = 1;
+        final boolean isBooting = mService.mAmInternal.isBooting();
+        final boolean isBooted = mService.mAmInternal.isBooted();
+        try {
+            mService.mAmInternal.setBooting(false);
+            mService.mAmInternal.setBooted(false);
+            mRootActivityContainer.onDisplayAdded(displayId);
+            verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt());
+        } finally {
+            mService.mAmInternal.setBooting(isBooting);
+            mService.mAmInternal.setBooted(isBooted);
+        }
+    }
+
+    /**
+     * Tests whether home can be started if being instrumented.
+     */
+    @Test
+    public void testCanStartHomeWhenInstrumented() {
+        final ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        final WindowProcessController app = mock(WindowProcessController.class);
+        doReturn(app).when(mService).getProcessController(any(), anyInt());
+
+        // Can not start home if we don't want to start home while home is being instrumented.
+        doReturn(true).when(app).isInstrumenting();
+        assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+
+        // Can start home for other cases.
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+
+        doReturn(false).when(app).isInstrumenting();
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                false /* allowInstrumenting*/));
+        assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY,
+                true /* allowInstrumenting*/));
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 0e1624e..a8b6dc3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -63,7 +63,7 @@
         final int numStacks = 2;
         for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
             final ActivityStack stack =
-                    new StackBuilder(mSupervisor).setCreateActivity(false).build();
+                    new StackBuilder(mRootActivityContainer).setCreateActivity(false).build();
             display.addChild(stack, POSITION_BOTTOM);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index 584f269..83e7ee7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -16,16 +16,17 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
rename to services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6833dc5..d14f30d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -16,16 +16,17 @@
 
 package com.android.server.wm;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
@@ -225,11 +226,9 @@
             mTransaction = transaction;
             mParent = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface parent")
-                    .setSize(3000, 3000)
                     .build();
             mSurface = wm.makeSurfaceBuilder(mSession)
                     .setName("test surface")
-                    .setSize(1, 1)
                     .build();
             mFinishedCallbackCalled = false;
             mLeash = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 6638eeb..9569c0d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -175,7 +175,7 @@
                 WINDOWING_MODE_FREEFORM);
         ActivityRecord source = createSourceActivity(freeformDisplay);
 
-        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTask(), null /* layout */,
+        assertEquals(RESULT_CONTINUE, mTarget.onCalculate(source.getTaskRecord(), null /* layout */,
                 null /* activity */, null /* source */, null /* options */, mCurrent, mResult));
 
         assertEquals(freeformDisplay.mDisplayId, mResult.mPreferredDisplayId);
@@ -888,10 +888,10 @@
 
     @Test
     public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() {
-        final Configuration overrideConfig = mSupervisor.getOverrideConfiguration();
+        final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration();
         // Egyptian Arabic is a RTL language.
         overrideConfig.setLayoutDirection(new Locale("ar", "EG"));
-        mSupervisor.onOverrideConfigurationChanged(overrideConfig);
+        mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig);
 
         final TestActivityDisplay freeformDisplay = createNewActivityDisplay(
                 WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 785b955..b996bfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -52,8 +52,8 @@
     private static final boolean DEBUGGING = false;
     private static final String TAG = "TaskPositionerTest";
 
-    private final static int MOUSE_DELTA_X = 5;
-    private final static int MOUSE_DELTA_Y = 5;
+    private static final int MOUSE_DELTA_X = 5;
+    private static final int MOUSE_DELTA_Y = 5;
 
     private int mMinVisibleWidth;
     private int mMinVisibleHeight;
@@ -315,7 +315,7 @@
         // Drag all the way to the right and see the height also shrinking.
         mPositioner.resizeDrag(2000.0f, midY);
         final int w = mMinVisibleWidth;
-        final int h = Math.round((float)w / MIN_ASPECT);
+        final int h = Math.round((float) w / MIN_ASPECT);
         assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
                 mPositioner.getWindowDragBounds());
 
@@ -428,7 +428,7 @@
         // Drag all the way to the right.
         mPositioner.resizeDrag(2000.0f, midY);
         w = mMinVisibleWidth;
-        h = Math.max(Math.round((float)w * MIN_ASPECT), r.height());
+        h = Math.max(Math.round((float) w * MIN_ASPECT), r.height());
         assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h),
                 mPositioner.getWindowDragBounds());
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 00b4629..3991e06 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -18,14 +18,15 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index d2c0765..792e8a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -48,7 +48,7 @@
     public void testGetClosingApps_closing() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -64,9 +64,9 @@
                 "closingWindow");
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
-        openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+        openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
@@ -79,7 +79,7 @@
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
         final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
                 "closingWindow");
-        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+        closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET,
                 true /* performLayout */, false /* isVoiceInteraction */);
         final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mAppToken);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index a569b9e..624ef9b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.server.wm;
@@ -20,14 +20,15 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.ComponentName;
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
index 1af79e4..bbf508d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import android.platform.test.annotations.Presubmit;
@@ -37,6 +35,7 @@
 @Presubmit
 public class TaskWindowContainerControllerTests extends WindowTestsBase {
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -49,7 +48,9 @@
         assertNull(taskController.mContainer);
         assertNull(appController.mContainer);
     }
+    */
 
+    /* Comment out due to removal of AppWindowContainerController
     @Test
     public void testRemoveContainer_deferRemoval() {
         final WindowTestUtils.TestTaskWindowContainerController taskController =
@@ -74,6 +75,7 @@
         assertNull(appController.mContainer);
         assertNull(app.getController());
     }
+    */
 
     @Test
     public void testReparent() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
rename to services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 99deeb9..29738ff 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -24,6 +24,8 @@
 import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -41,6 +43,15 @@
     }
 
     @Override
+    public void insetsChanged(InsetsState insetsState) throws RemoteException {
+    }
+
+    @Override
+    public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls)
+            throws RemoteException {
+    }
+
+    @Override
     public void moved(int newX, int newY) throws RemoteException {
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
rename to services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 7b542cb..ba81bd1 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,8 +19,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -46,7 +46,8 @@
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private final Supplier<WindowManagerService> mWmSupplier;
 
-    boolean keyguardShowingAndNotOccluded = false;
+    int mRotationToReport = 0;
+    boolean mKeyguardShowingAndNotOccluded = false;
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
@@ -236,7 +237,7 @@
 
     @Override
     public boolean isKeyguardLocked() {
-        return keyguardShowingAndNotOccluded;
+        return mKeyguardShowingAndNotOccluded;
     }
 
     @Override
@@ -256,7 +257,7 @@
 
     @Override
     public boolean isKeyguardShowingAndNotOccluded() {
-        return keyguardShowingAndNotOccluded;
+        return mKeyguardShowingAndNotOccluded;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
rename to services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
index 9e22c0a..612f9ad 100644
--- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java
@@ -73,7 +73,7 @@
     public void testClear() {
         final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
         mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token);
-        mDisplayContent.mUnknownAppVisibilityController.clear();;
+        mDisplayContent.mUnknownAppVisibilityController.clear();
         assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index 4ea6b39..d07230e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -18,11 +18,12 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
 import static junit.framework.TestCase.assertNotNull;
 
 import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.graphics.Bitmap;
 import android.os.IBinder;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 3643457..885a7e0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -231,7 +231,7 @@
 
         final Configuration config = new Configuration();
         final WindowConfiguration winConfig = config.windowConfiguration;
-        stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/,
+        stackController.adjustConfigurationForBounds(bounds,
                 new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/,
                 false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig,
                 windowingMode);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index e59afd6..d4a32cf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,13 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -30,13 +37,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -459,13 +459,13 @@
     @Test
     public void testGetOrientation_childSpecified() {
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE,
-            SCREEN_ORIENTATION_LANDSCAPE);
+                SCREEN_ORIENTATION_LANDSCAPE);
         testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET,
-            SCREEN_ORIENTATION_UNSPECIFIED);
+                SCREEN_ORIENTATION_UNSPECIFIED);
     }
 
     private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation,
-        int expectedOrientation) {
+            int expectedOrientation) {
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = builder.setLayer(0).build();
         root.setFillsParent(true);
@@ -704,7 +704,7 @@
         final TestWindowContainer root = builder.build();
 
         final TestWindowContainer child = root.addChildWindow();
-        child.setBounds(new Rect(1,1,2,2));
+        child.setBounds(new Rect(1, 1, 2, 2));
 
         final TestWindowContainer grandChild = mock(TestWindowContainer.class);
 
@@ -742,7 +742,7 @@
         private static final Comparator<TestWindowContainer> SUBLAYER_COMPARATOR = (w1, w2) -> {
             final int layer1 = w1.mLayer;
             final int layer2 = w2.mLayer;
-            if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) {
+            if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) {
                 // We insert the child window into the list ordered by the mLayer. For same layers,
                 // the negative one should go below others; the positive one should go above others.
                 return -1;
@@ -782,7 +782,7 @@
         }
 
         TestWindowContainer addChildWindow() {
-            return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1));
+            return addChildWindow(new TestWindowContainerBuilder(mWmService).setLayer(1));
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index fcde08e..4b666f5 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -21,9 +21,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 
 import android.platform.test.annotations.Presubmit;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 227eb00..60a8aeb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -76,7 +76,7 @@
 
     private static class TaskWithBounds extends Task {
         final Rect mBounds;
-        final Rect mInsetBounds = new Rect();
+        final Rect mOverrideDisplayedBounds = new Rect();
         boolean mFullscreenForTest = true;
 
         TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) {
@@ -100,8 +100,8 @@
             outBounds.set(mBounds);
         }
         @Override
-        void getTempInsetBounds(Rect outBounds) {
-            outBounds.set(mInsetBounds);
+        Rect getOverrideDisplayedBounds() {
+            return mOverrideDisplayedBounds;
         }
         @Override
         boolean isFullscreen() {
@@ -290,7 +290,7 @@
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
         w.computeFrameLw();
-         assertFrame(w, 700, 0, 1000, 300);
+        assertFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
         w.computeFrameLw();
         assertFrame(w, 700, 700, 1000, 1000);
@@ -343,14 +343,14 @@
                 taskBottom - contentInsetBottom));
 
         pf.set(0, 0, logicalWidth, logicalHeight);
-        // However if we set temp inset bounds, the insets will be computed
-        // as if our window was laid out there,  but it will be laid out according to
-        // the task bounds.
+        // If we set displayed bounds, the insets will be computed with the main task bounds
+        // but the frame will be positioned according to the displayed bounds.
         final int insetLeft = logicalWidth / 5;
         final int insetTop = logicalHeight / 5;
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
-        task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
+        task.mOverrideDisplayedBounds.set(taskBounds);
+        task.mBounds.set(insetLeft, insetTop, insetRight, insetBottom);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw();
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -430,7 +430,6 @@
         final int taskBottom = logicalHeight / 4 * 3;
         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
         final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds);
-        task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
         task.mFullscreenForTest = false;
         WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
@@ -486,12 +485,12 @@
     }
 
     @Test
-    public void testDisplayCutout_tempInsetBounds() {
+    public void testDisplayCutout_tempDisplayedBounds() {
         // Regular fullscreen task and window
         final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm,
-                new Rect(0, -500, 1000, 1500));
+                new Rect(0, 0, 1000, 2000));
         task.mFullscreenForTest = false;
-        task.mInsetBounds.set(0, 0, 1000, 2000);
+        task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500));
         WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT);
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
index 9a13efb..266d884 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
@@ -21,14 +21,15 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManagerInternal;
 import android.content.Context;
@@ -100,6 +101,7 @@
                         mock(PowerManagerInternal.class));
                 final PowerManagerInternal pm =
                         LocalServices.getService(PowerManagerInternal.class);
+                doNothing().when(pm).registerLowPowerModeObserver(any());
                 PowerSaveState state = new PowerSaveState.Builder().build();
                 doReturn(state).when(pm).getLowPowerState(anyInt());
 
@@ -146,8 +148,8 @@
 
                 final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
                 final DisplayWindowController dcw = new DisplayWindowController(display, mService);
-                // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor.
-                // We emulate those steps here.
+                // Display creation is driven by the ActivityManagerService via
+                // ActivityStackSupervisor. We emulate those steps here.
                 mService.mRoot.createDisplayContent(display, dcw);
             }
 
@@ -171,21 +173,21 @@
         };
     }
 
-    public WindowManagerService getWindowManagerService() {
+    WindowManagerService getWindowManagerService() {
         return mService;
     }
 
-    public TestWindowManagerPolicy getWindowManagerPolicy() {
-        return mPolicy;
-    }
-
-    public void waitUntilWindowManagerHandlersIdle() {
+    void waitUntilWindowManagerHandlersIdle() {
         final WindowManagerService wm = getWindowManagerService();
-        if (wm != null) {
-            wm.mH.runWithScissors(() -> { }, 0);
-            wm.mAnimationHandler.runWithScissors(() -> { }, 0);
-            SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
+        if (wm == null) {
+            return;
         }
+        wm.mH.removeCallbacksAndMessages(null);
+        wm.mAnimationHandler.removeCallbacksAndMessages(null);
+        SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+        wm.mH.runWithScissors(() -> { }, 0);
+        wm.mAnimationHandler.runWithScissors(() -> { }, 0);
+        SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
     }
 
     private void destroyAllSurfaceTransactions() {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 118ce89..7f78034 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -32,6 +32,12 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.assertEquals;
@@ -43,11 +49,6 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
 
 import android.graphics.Insets;
 import android.graphics.Matrix;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index e56edab..aa0ecf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
@@ -23,12 +26,24 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IBinder;
 import android.view.Display;
+import android.view.IApplicationToken;
+import android.view.IWindow;
 import android.view.Surface;
+import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
 
 import org.mockito.invocation.InvocationOnMock;
 
@@ -37,6 +52,7 @@
  * to WindowManager related test functionality.
  */
 public class WindowTestUtils {
+    private static int sNextTaskId = 0;
 
     /** An extension of {@link DisplayContent} to gain package scoped access. */
     public static class TestDisplayContent extends DisplayContent {
@@ -54,6 +70,14 @@
             return null;
         }
 
+        /**
+         * Stubbing method of non-public parent class isn't supported, so here explicitly overrides.
+         */
+        @Override
+        DockedStackDividerController getDockedDividerController() {
+            return null;
+        }
+
         /** Create a mocked default {@link DisplayContent}. */
         public static TestDisplayContent create(Context context) {
             final TestDisplayContent displayContent = mock(TestDisplayContent.class);
@@ -65,7 +89,7 @@
 
             final DisplayRotation displayRotation = new DisplayRotation(
                     mock(WindowManagerService.class), displayContent, displayPolicy,
-                    context, new Object());
+                    mock(DisplayWindowSettings.class), context, new Object());
             displayRotation.mPortraitRotation = Surface.ROTATION_0;
             displayRotation.mLandscapeRotation = Surface.ROTATION_90;
             displayRotation.mUpsideDownRotation = Surface.ROTATION_180;
@@ -96,16 +120,27 @@
         // many components rely on the {@link StackWindowController#adjustConfigurationForBounds}
         // to properly set bounds values in the configuration. We must mimick those actions here.
         doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Configuration config = invocationOnMock.<Configuration>getArgument(7);
+            final Configuration config = invocationOnMock.<Configuration>getArgument(6);
             final Rect bounds = invocationOnMock.<Rect>getArgument(0);
             config.windowConfiguration.setBounds(bounds);
             return null;
-        }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(),
+        }).when(controller).adjustConfigurationForBounds(any(), any(), any(),
                 anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt());
 
         return controller;
     }
 
+    /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
+    public static Task createTaskInStack(WindowManagerService service, TaskStack stack,
+            int userId) {
+        synchronized (service.mGlobalLock) {
+            final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false,
+                    new ActivityManager.TaskDescription(), null);
+            stack.addTask(newTask, POSITION_TOP);
+            return newTask;
+        }
+    }
+
     /**
      * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not
      * normally be mocked out.
@@ -120,4 +155,233 @@
             // Do nothing.
         }
     }
+
+    static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) {
+        synchronized (dc.mWmService.mGlobalLock) {
+            return new TestAppWindowToken(dc);
+        }
+    }
+
+    /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
+    public static class TestAppWindowToken extends AppWindowToken {
+        boolean mOnTop = false;
+        private Transaction mPendingTransactionOverride;
+
+        private TestAppWindowToken(DisplayContent dc) {
+            super(dc.mWmService, new IApplicationToken.Stub() {
+                @Override
+                public String getName() {
+                    return null;
+                }
+            }, new ComponentName("", ""), false, dc, true /* fillsParent */);
+        }
+
+        TestAppWindowToken(WindowManagerService service, IApplicationToken token,
+                ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc,
+                long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers,
+                int targetSdk, int orientation, int rotationAnimationHint, int configChanges,
+                boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) {
+            super(service, token, activityComponent, voiceInteraction, dc,
+                    inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk,
+                    orientation, rotationAnimationHint, configChanges, launchTaskBehind,
+                    alwaysFocusable, activityRecord);
+        }
+
+        int getWindowsCount() {
+            return mChildren.size();
+        }
+
+        boolean hasWindow(WindowState w) {
+            return mChildren.contains(w);
+        }
+
+        WindowState getFirstChild() {
+            return mChildren.peekFirst();
+        }
+
+        WindowState getLastChild() {
+            return mChildren.peekLast();
+        }
+
+        int positionInParent() {
+            return getParent().mChildren.indexOf(this);
+        }
+
+        void setIsOnTop(boolean onTop) {
+            mOnTop = onTop;
+        }
+
+        @Override
+        boolean isOnTop() {
+            return mOnTop;
+        }
+
+        void setPendingTransaction(Transaction transaction) {
+            mPendingTransactionOverride = transaction;
+        }
+
+        @Override
+        public Transaction getPendingTransaction() {
+            return mPendingTransactionOverride == null
+                    ? super.getPendingTransaction()
+                    : mPendingTransactionOverride;
+        }
+    }
+
+    static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
+        return createTestWindowToken(type, dc, false /* persistOnEmpty */);
+    }
+
+    static TestWindowToken createTestWindowToken(int type, DisplayContent dc,
+            boolean persistOnEmpty) {
+        synchronized (dc.mWmService.mGlobalLock) {
+            return new TestWindowToken(type, dc, persistOnEmpty);
+        }
+    }
+
+    /* Used so we can gain access to some protected members of the {@link WindowToken} class */
+    public static class TestWindowToken extends WindowToken {
+
+        private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) {
+            super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc,
+                    false /* ownerCanManageAppTokens */);
+        }
+
+        int getWindowsCount() {
+            return mChildren.size();
+        }
+
+        boolean hasWindow(WindowState w) {
+            return mChildren.contains(w);
+        }
+    }
+
+    /* Used so we can gain access to some protected members of the {@link Task} class */
+    public static class TestTask extends Task {
+        boolean mShouldDeferRemoval = false;
+        boolean mOnDisplayChangedCalled = false;
+        private boolean mIsAnimating = false;
+
+        TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service,
+                int resizeMode, boolean supportsPictureInPicture,
+                TaskWindowContainerController controller) {
+            super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture,
+                    new ActivityManager.TaskDescription(), controller);
+        }
+
+        boolean shouldDeferRemoval() {
+            return mShouldDeferRemoval;
+        }
+
+        int positionInParent() {
+            return getParent().mChildren.indexOf(this);
+        }
+
+        @Override
+        void onDisplayChanged(DisplayContent dc) {
+            super.onDisplayChanged(dc);
+            mOnDisplayChangedCalled = true;
+        }
+
+        @Override
+        boolean isSelfAnimating() {
+            return mIsAnimating;
+        }
+
+        void setLocalIsAnimating(boolean isAnimating) {
+            mIsAnimating = isAnimating;
+        }
+    }
+
+    /**
+     * Used so we can gain access to some protected members of {@link TaskWindowContainerController}
+     * class.
+     */
+    public static class TestTaskWindowContainerController extends TaskWindowContainerController {
+
+        static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() {
+            @Override
+            public void registerConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void unregisterConfigurationChangeListener(
+                    ConfigurationContainerListener listener) {
+            }
+
+            @Override
+            public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) {
+            }
+
+            @Override
+            public void requestResize(Rect bounds, int resizeMode) {
+            }
+        };
+
+        TestTaskWindowContainerController(WindowTestsBase testsBase) {
+            this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent));
+        }
+
+        TestTaskWindowContainerController(StackWindowController stackController) {
+            super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */,
+                    RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/,
+                    true /* showForAllUsers */, new ActivityManager.TaskDescription(),
+                    stackController.mService);
+        }
+
+        @Override
+        TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode,
+                boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) {
+            return new TestTask(taskId, stack, userId, mService, resizeMode,
+                    supportsPictureInPicture, this);
+        }
+    }
+
+    public static class TestIApplicationToken implements IApplicationToken {
+
+        private final Binder mBinder = new Binder();
+        @Override
+        public IBinder asBinder() {
+            return mBinder;
+        }
+        @Override
+        public String getName() {
+            return null;
+        }
+    }
+
+    /** Used to track resize reports. */
+    public static class TestWindowState extends WindowState {
+        boolean mResizeReported;
+
+        TestWindowState(WindowManagerService service, Session session, IWindow window,
+                WindowManager.LayoutParams attrs, WindowToken token) {
+            super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0,
+                    false /* ownerCanAddInternalSystemWindow */);
+        }
+
+        @Override
+        void reportResized() {
+            super.reportResized();
+            mResizeReported = true;
+        }
+
+        @Override
+        public boolean isGoneForLayoutLw() {
+            return false;
+        }
+
+        @Override
+        void updateResizingWindowIfNeeded() {
+            // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
+            // the system that it can actually update the window.
+            boolean hadSurface = mHasSurface;
+            mHasSurface = true;
+
+            super.updateResizingWindowIfNeeded();
+
+            mHasSurface = hadSurface;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 1eb46fb..b3f56e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -37,7 +37,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static org.mockito.Mockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 
 import android.content.Context;
 import android.content.res.Configuration;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java
rename to services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
rename to services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 0c40a6b..de40e0d 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -24,7 +24,6 @@
         "libdexfile",
         "slicer",
     ],
-    cppflags: ["-std=c++17"],
 }
 
 cc_library_host_static {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index cef99865..d617de0 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -29,7 +29,6 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
-import java.lang.String;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
@@ -908,10 +907,16 @@
         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
-            sb.append("[pa: ");
+            sb.append("[id: ");
+            sb.append(mTelecomCallId);
+            sb.append(", pa: ");
             sb.append(mAccountHandle);
             sb.append(", hdl: ");
-            sb.append(Log.pii(mHandle));
+            sb.append(Log.piiHandle(mHandle));
+            sb.append(", hdlPres: ");
+            sb.append(mHandlePresentation);
+            sb.append(", videoState: ");
+            sb.append(VideoProfile.videoStateToString(mVideoState));
             sb.append(", caps: ");
             sb.append(capabilitiesToString(mCallCapabilities));
             sb.append(", props: ");
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 7db6940..6628743 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -27,8 +27,8 @@
 import android.os.RemoteException;
 
 import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallScreeningService;
 import com.android.internal.telecom.ICallScreeningAdapter;
+import com.android.internal.telecom.ICallScreeningService;
 
 /**
  * This service can be implemented by the default dialer (see
@@ -147,7 +147,7 @@
             private boolean mShouldSkipCallLog;
             private boolean mShouldSkipNotification;
 
-            /*
+            /**
              * Sets whether the incoming call should be blocked.
              */
             public Builder setDisallowCall(boolean shouldDisallowCall) {
@@ -155,7 +155,7 @@
                 return this;
             }
 
-            /*
+            /**
              * Sets whether the incoming call should be disconnected as if the user had manually
              * rejected it. This property should only be set to true if the call is disallowed.
              */
@@ -164,16 +164,20 @@
                 return this;
             }
 
-            /*
+            /**
              * Sets whether the incoming call should not be displayed in the call log. This property
              * should only be set to true if the call is disallowed.
+             * <p>
+             * Note: Calls will still be logged with type
+             * {@link android.provider.CallLog.Calls#BLOCKED_TYPE}, regardless of how this property
+             * is set.
              */
             public Builder setSkipCallLog(boolean shouldSkipCallLog) {
                 mShouldSkipCallLog = shouldSkipCallLog;
                 return this;
             }
 
-            /*
+            /**
              * Sets whether a missed call notification should not be shown for the incoming call.
              * This property should only be set to true if the call is disallowed.
              */
@@ -211,6 +215,17 @@
      * Called when a new incoming call is added.
      * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
      * should be called to allow or disallow the call.
+     * <p>
+     * Note: The {@link Call.Details} instance provided to a call screening service will only have
+     * the following properties set.  The rest of the {@link Call.Details} properties will be set to
+     * their default value or {@code null}.
+     * <ul>
+     *     <li>{@link Call.Details#getState()}</li>
+     *     <li>{@link Call.Details#getConnectTimeMillis()}</li>
+     *     <li>{@link Call.Details#getCreationTimeMillis()}</li>
+     *     <li>{@link Call.Details#getHandle()}</li>
+     *     <li>{@link Call.Details#getHandlePresentation()}</li>
+     * </ul>
      *
      * @param callDetails Information about a new incoming call, see {@link Call.Details}.
      */
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d97f0c5..143b323 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -304,4 +304,10 @@
      * @see TelecomServiceImpl#handleCallIntent
      */
     void handleCallIntent(in Intent intent);
+
+    void setTestDefaultCallScreeningApp(String packageName);
+
+    void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
+
+    void setTestAutoModeApp(String packageName);
 }
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 054288b..2236cba 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -13,3 +13,4 @@
 shuoq@google.com
 refuhoo@google.com
 paulye@google.com
+nazaninb@google.com
\ No newline at end of file
diff --git a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
similarity index 69%
copy from packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
copy to telephony/java/android/telephony/AvailableNetworkInfo.aidl
index 5ca9d15..1d4378c 100644
--- a/packages/overlays/ExperimentNavigationBarSlimOverlay/res/values/strings.xml
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.aidl
@@ -1,6 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
+/*
  * Copyright (c) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Name of overlay [CHAR LIMIT=64] -->
-    <string name="experiment_navigationbar_overlay">Slim Navigation Bar Experiment</string>
-</resources>
\ No newline at end of file
+
+package android.telephony;
+
+parcelable AvailableNetworkInfo;
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
new file mode 100644
index 0000000..fe07370
--- /dev/null
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2018 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.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Defines available network information which includes corresponding subscription id,
+ * network plmns and corresponding priority to be used for network selection by Alternative Network
+ * Service.
+ */
+public final class AvailableNetworkInfo implements Parcelable {
+
+    /*
+     * Defines number of priority level high.
+     */
+    public static final int PRIORITY_HIGH = 1;
+
+    /*
+     * Defines number of priority level medium.
+     */
+    public static final int PRIORITY_MED = 2;
+
+    /*
+     * Defines number of priority level low.
+     */
+    public static final int PRIORITY_LOW = 3;
+
+    /**
+     * subscription Id of the available network. This value must be one of the entry retrieved from
+     * {@link SubscriptionManager#getOpportunisticSubscriptions}
+     */
+    private int mSubId;
+
+    /**
+     * Priority for the subscription id.
+     * Priorities are in the range of 1 to 3 where 1
+     * has the highest priority.
+     */
+    private int mPriority;
+
+    /**
+     * Describes the List of PLMN ids (MCC-MNC) associated with mSubId.
+     * If this entry is left empty, then the platform software will not scan the network
+     * to revalidate the input.
+     */
+    private ArrayList<String> mMccMncs;
+
+    /**
+     * Return subscription Id of the available network.
+     * This value must be one of the entry retrieved from
+     * {@link SubscriptionManager#getOpportunisticSubscriptions}
+     * @return subscription id
+     */
+    public int getSubId() {
+        return mSubId;
+    }
+
+    /**
+     * Return priority for the subscription id. Valid value will be within
+     * [{@link AvailableNetworkInfo#PRIORITY_HIGH}, {@link AvailableNetworkInfo#PRIORITY_LOW}]
+     * @return priority level
+     */
+    public int getPriority() {
+        return mPriority;
+    }
+
+    /**
+     * Return List of PLMN ids (MCC-MNC) associated with the sub ID.
+     * If this entry is left empty, then the platform software will not scan the network
+     * to revalidate the input.
+     * @return list of PLMN ids
+     */
+    public List<String> getMccMncs() {
+        return (List<String>) mMccMncs.clone();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSubId);
+        dest.writeInt(mPriority);
+        dest.writeStringList(mMccMncs);
+    }
+
+    private AvailableNetworkInfo(Parcel in) {
+        mSubId = in.readInt();
+        mPriority = in.readInt();
+        in.readStringList(mMccMncs);
+    }
+
+    public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+        mSubId = subId;
+        mPriority = priority;
+        mMccMncs = new ArrayList<String>(mccMncs);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        AvailableNetworkInfo ani;
+
+        try {
+            ani = (AvailableNetworkInfo) o;
+        } catch (ClassCastException ex) {
+            return false;
+        }
+
+        if (o == null) {
+            return false;
+        }
+
+        return (mSubId == ani.mSubId
+                && mPriority == ani.mPriority
+                && (((mMccMncs != null)
+                && mMccMncs.equals(ani.mMccMncs))));
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSubId, mPriority, mMccMncs);
+    }
+
+    public static final Parcelable.Creator<AvailableNetworkInfo> CREATOR =
+            new Creator<AvailableNetworkInfo>() {
+                @Override
+                public AvailableNetworkInfo createFromParcel(Parcel in) {
+                    return new AvailableNetworkInfo(in);
+                }
+
+                @Override
+                public AvailableNetworkInfo[] newArray(int size) {
+                    return new AvailableNetworkInfo[size];
+                }
+            };
+
+    @Override
+    public String toString() {
+        return ("AvailableNetworkInfo:"
+                + " mSubId: " + mSubId
+                + " mPriority: " + mPriority
+                + " mMccMncs: " + Arrays.toString(mMccMncs.toArray()));
+    }
+}
+
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 185c886..62cead1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1154,20 +1154,33 @@
      */
     public static final String KEY_CARRIER_NAME_STRING = "carrier_name_string";
 
- /**
-  * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
-  * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
-  * the carrier
-  * CallScreeningService with the opportunity to allow or block calls.
-  * <p>
-  * The String includes the package name/the class name.
-  * Example:
-  * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
-  * <p>
-  * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
-  * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
-  * ComponentName.
-  */
+    /**
+     * String to override sim country iso.
+     * Sim country iso is based on sim MCC which is coarse and doesn't work with dual IMSI SIM where
+     * a SIM can have multiple MCC from different countries.
+     * Instead, each sim carrier should have a single country code, apply per carrier based iso
+     * code as an override. The overridden value can be read from
+     * {@link TelephonyManager#getSimCountryIso()} and {@link SubscriptionInfo#getCountryIso()}
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING =
+            "sim_country_iso_override_string";
+
+   /**
+    * The Component Name of a carrier-provided CallScreeningService implementation. Telecom will
+    * bind to {@link android.telecom.CallScreeningService} for ALL incoming calls and provide
+    * the carrier
+    * CallScreeningService with the opportunity to allow or block calls.
+    * <p>
+    * The String includes the package name/the class name.
+    * Example:
+    * <item>com.android.carrier/com.android.carrier.callscreeningserviceimpl</item>
+    * <p>
+    * Using {@link ComponentName#flattenToString()} to convert a ComponentName object to String.
+    * Using {@link ComponentName#unflattenFromString(String)} to convert a String object to a
+    * ComponentName.
+    */
     public static final String KEY_CARRIER_CALL_SCREENING_APP_STRING = "call_screening_app";
 
     /**
@@ -2533,6 +2546,7 @@
         sDefaults.putBoolean(KEY_CONFIG_WIFI_DISABLE_IN_ECBM, false);
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
+        sDefaults.putString(KEY_SIM_COUNTRY_ISO_OVERRIDE_STRING, "");
         sDefaults.putString(KEY_CARRIER_CALL_SCREENING_APP_STRING, "");
         sDefaults.putBoolean(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CDMA_HOME_REGISTERED_PLMN_NAME_STRING, "");
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
index b6e6cba..5d809d0 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java
@@ -33,17 +33,31 @@
      */
     public final boolean isNrAvailable;
 
+    /**
+     * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving
+     * cell.
+     *
+     * True the primary serving cell is LTE cell and the plmn-InfoList-r15 is present in SIB2 and
+     * at least one bit in this list is true, otherwise this value should be false.
+     *
+     * Reference: 3GPP TS 36.331 v15.2.2 6.3.1 System information blocks.
+     */
+    public final boolean isEnDcAvailable;
+
     DataSpecificRegistrationStates(
-            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable) {
+            int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
+            boolean isEnDcAvailable) {
         this.maxDataCalls = maxDataCalls;
         this.isDcNrRestricted = isDcNrRestricted;
         this.isNrAvailable = isNrAvailable;
+        this.isEnDcAvailable = isEnDcAvailable;
     }
 
     private DataSpecificRegistrationStates(Parcel source) {
         maxDataCalls = source.readInt();
         isDcNrRestricted = source.readBoolean();
         isNrAvailable = source.readBoolean();
+        isEnDcAvailable = source.readBoolean();
     }
 
     @Override
@@ -51,6 +65,7 @@
         dest.writeInt(maxDataCalls);
         dest.writeBoolean(isDcNrRestricted);
         dest.writeBoolean(isNrAvailable);
+        dest.writeBoolean(isEnDcAvailable);
     }
 
     @Override
@@ -65,13 +80,14 @@
                 .append(" maxDataCalls = " + maxDataCalls)
                 .append(" isDcNrRestricted = " + isDcNrRestricted)
                 .append(" isNrAvailable = " + isNrAvailable)
+                .append(" isEnDcAvailable = " + isEnDcAvailable)
                 .append(" }")
                 .toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable);
+        return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable);
     }
 
     @Override
@@ -83,7 +99,8 @@
         DataSpecificRegistrationStates other = (DataSpecificRegistrationStates) o;
         return this.maxDataCalls == other.maxDataCalls
                 && this.isDcNrRestricted == other.isDcNrRestricted
-                && this.isNrAvailable == other.isNrAvailable;
+                && this.isNrAvailable == other.isNrAvailable
+                && this.isEnDcAvailable == other.isEnDcAvailable;
     }
 
     public static final Parcelable.Creator<DataSpecificRegistrationStates> CREATOR =
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index aee744f..b00665e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -219,12 +219,13 @@
     public NetworkRegistrationState(int domain, int transportType, int regState,
             int accessNetworkTechnology, int rejectCause, boolean emergencyOnly,
             int[] availableServices, @Nullable CellIdentity cellIdentity, int maxDataCalls,
-            boolean isDcNrRestricted, boolean isNrAvailable) {
+            boolean isDcNrRestricted, boolean isNrAvailable, boolean isEndcAvailable) {
         this(domain, transportType, regState, accessNetworkTechnology, rejectCause, emergencyOnly,
                 availableServices, cellIdentity);
 
         mDataSpecificStates = new DataSpecificRegistrationStates(
-                maxDataCalls, isDcNrRestricted, isNrAvailable);
+                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable);
+        updateNrStatus(mDataSpecificStates);
     }
 
     protected NetworkRegistrationState(Parcel source) {
@@ -448,6 +449,34 @@
         dest.writeInt(mNrStatus);
     }
 
+    /**
+     * Use the 5G NR Non-Standalone indicators from the network registration state to update the
+     * NR status. There are 3 indicators in the network registration state:
+     *
+     * 1. if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving cell.
+     * 2. if NR is supported by the selected PLMN.
+     * 3. if the use of dual connectivity with NR is restricted.
+     *
+     * The network has 5G NR capability if E-UTRA-NR Dual Connectivity is supported by the primary
+     * serving cell.
+     *
+     * The use of NR 5G is not restricted If the network has 5G NR capability and both the use of
+     * DCNR is not restricted and NR is supported by the selected PLMN. Otherwise the use of 5G
+     * NR is restricted.
+     *
+     * @param state data specific registration state contains the 5G NR indicators.
+     */
+    private void updateNrStatus(DataSpecificRegistrationStates state) {
+        mNrStatus = NR_STATUS_NONE;
+        if (state.isEnDcAvailable) {
+            if (!state.isDcNrRestricted && state.isNrAvailable) {
+                mNrStatus = NR_STATUS_NOT_RESTRICTED;
+            } else {
+                mNrStatus = NR_STATUS_RESTRICTED;
+            }
+        }
+    }
+
     public static final Parcelable.Creator<NetworkRegistrationState> CREATOR =
             new Parcelable.Creator<NetworkRegistrationState>() {
         @Override
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
new file mode 100644
index 0000000..b00c573
--- /dev/null
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+/**
+ * A callback for number verification. After a request for number verification is received,
+ * the system will call {@link #onCallReceived(String)} if a phone call was received from a number
+ * matching the provided {@link PhoneNumberRange} or it will call {@link #onVerificationFailed(int)}
+ * if an error occurs.
+ * @hide
+ */
+@SystemApi
+public interface NumberVerificationCallback {
+    /** @hide */
+    @IntDef(value = {REASON_UNSPECIFIED, REASON_TIMED_OUT, REASON_NETWORK_NOT_AVAILABLE,
+            REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
+            REASON_IN_EMERGENCY_CALL},
+            prefix = {"REASON_"})
+    @interface NumberVerificationFailureReason {}
+
+    /**
+     * Verification failed for an unspecified reason.
+     */
+    int REASON_UNSPECIFIED = 0;
+
+    /**
+     * Verification failed because no phone call was received from a matching number within the
+     * provided timeout.
+     */
+    int REASON_TIMED_OUT = 1;
+
+    /**
+     * Verification failed because no cellular voice network is available.
+     */
+    int REASON_NETWORK_NOT_AVAILABLE = 2;
+
+    /**
+     * Verification failed because there are currently too many ongoing phone calls for a new
+     * incoming phone call to be received.
+     */
+    int REASON_TOO_MANY_CALLS = 3;
+
+    /**
+     * Verification failed because a previous request for verification has not yet completed.
+     */
+    int REASON_CONCURRENT_REQUESTS = 4;
+
+    /**
+     * Verification failed because the phone is in emergency callback mode.
+     */
+    int REASON_IN_ECBM = 5;
+
+    /**
+     * Verification failed because the phone is currently in an emergency call.
+     */
+    int REASON_IN_EMERGENCY_CALL = 6;
+
+    /**
+     * Called when the device receives a phone call from the provided {@link PhoneNumberRange}.
+     * @param phoneNumber The phone number within the range that called. May or may not contain the
+     *                    country code, but will be entirely numeric.
+     */
+    default void onCallReceived(@NonNull String phoneNumber) { }
+
+    /**
+     * Called when verification fails for some reason.
+     * @param reason The reason for failure.
+     */
+    default void onVerificationFailed(@NumberVerificationFailureReason int reason) { }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/telephony/java/android/telephony/PhoneNumberRange.aidl
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to telephony/java/android/telephony/PhoneNumberRange.aidl
index 27d25b8..b0727be 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/telephony/java/android/telephony/PhoneNumberRange.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright 2018 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.
@@ -13,12 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
-}
+package android.telephony;
+
+parcelable PhoneNumberRange;
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
new file mode 100644
index 0000000..d65156f
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.regex.Pattern;
+
+/**
+ * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
+ * block of phone numbers.
+ *
+ * Example:
+ * {@code
+ * {
+ *     mCountryCode = "1"
+ *     mPrefix = "650555"
+ *     mLowerBound = "0055"
+ *     mUpperBound = "0899"
+ * }
+ * }
+ * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
+ * @hide
+ */
+@SystemApi
+public final class PhoneNumberRange implements Parcelable {
+    public static final Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
+        @Override
+        public PhoneNumberRange createFromParcel(Parcel in) {
+            return new PhoneNumberRange(in);
+        }
+
+        @Override
+        public PhoneNumberRange[] newArray(int size) {
+            return new PhoneNumberRange[size];
+        }
+    };
+
+    private final String mCountryCode;
+    private final String mPrefix;
+    private final String mLowerBound;
+    private final String mUpperBound;
+
+    /**
+     * @param countryCode The country code, omitting the leading "+"
+     * @param prefix A prefix that all numbers matching the range must have.
+     * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
+     *                   numbers that match this range.
+     * @param upperBound When concatenated with the prefix, represents the upper bound of phone
+     *                   numbers that match this range.
+     */
+    public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
+            @NonNull String lowerBound, @NonNull String upperBound) {
+        validateLowerAndUpperBounds(lowerBound, upperBound);
+        if (!Pattern.matches("[0-9]+", countryCode)) {
+            throw new IllegalArgumentException("Country code must be all numeric");
+        }
+        if (!Pattern.matches("[0-9]+", prefix)) {
+            throw new IllegalArgumentException("Prefix must be all numeric");
+        }
+        mCountryCode = countryCode;
+        mPrefix = prefix;
+        mLowerBound = lowerBound;
+        mUpperBound = upperBound;
+    }
+
+    private PhoneNumberRange(Parcel in) {
+        mCountryCode = in.readStringNoHelper();
+        mPrefix = in.readStringNoHelper();
+        mLowerBound = in.readStringNoHelper();
+        mUpperBound = in.readStringNoHelper();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStringNoHelper(mCountryCode);
+        dest.writeStringNoHelper(mPrefix);
+        dest.writeStringNoHelper(mLowerBound);
+        dest.writeStringNoHelper(mUpperBound);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PhoneNumberRange that = (PhoneNumberRange) o;
+        return Objects.equals(mCountryCode, that.mCountryCode)
+                && Objects.equals(mPrefix, that.mPrefix)
+                && Objects.equals(mLowerBound, that.mLowerBound)
+                && Objects.equals(mUpperBound, that.mUpperBound);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
+    }
+
+    @Override
+    public String toString() {
+        return "PhoneNumberRange{"
+                + "mCountryCode='" + mCountryCode + '\''
+                + ", mPrefix='" + mPrefix + '\''
+                + ", mLowerBound='" + mLowerBound + '\''
+                + ", mUpperBound='" + mUpperBound + '\''
+                + '}';
+    }
+
+    private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
+        if (lowerBound.length() != upperBound.length()) {
+            throw new IllegalArgumentException("Lower and upper bounds must have the same length");
+        }
+        if (!Pattern.matches("[0-9]+", lowerBound)) {
+            throw new IllegalArgumentException("Lower bound must be all numeric");
+        }
+        if (!Pattern.matches("[0-9]+", upperBound)) {
+            throw new IllegalArgumentException("Upper bound must be all numeric");
+        }
+        if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
+            throw new IllegalArgumentException("Lower bound must be lower than upper bound");
+        }
+    }
+
+    /**
+     * Checks to see if the provided phone number matches this range.
+     * @param number A phone number, with or without separators or a country code.
+     * @return {@code true} if the number matches, {@code false} otherwise.
+     */
+    public boolean matches(String number) {
+        // Check the prefix, make sure it matches either with or without the country code.
+        String normalizedNumber = number.replaceAll("[^0-9]", "");
+        String prefixWithCountryCode = mCountryCode + mPrefix;
+        String numberPostfix;
+        if (normalizedNumber.startsWith(prefixWithCountryCode)) {
+            numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
+        } else if (normalizedNumber.startsWith(mPrefix)) {
+            numberPostfix = normalizedNumber.substring(mPrefix.length());
+        } else {
+            return false;
+        }
+
+        // Next check the postfix to make sure it lies within the bounds.
+        try {
+            int lower = Integer.parseInt(mLowerBound);
+            int upper = Integer.parseInt(mUpperBound);
+            int numberToCheck = Integer.parseInt(numberPostfix);
+            return numberToCheck <= upper && numberToCheck >= lower;
+        } catch (NumberFormatException e) {
+            Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
+            return false;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index d2001ae..22ddb4a 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -16,11 +16,15 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.annotation.IntDef;
+import android.telephony.TelephonyManager.NetworkType;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * @hide
@@ -50,6 +54,7 @@
      *
      * <p>One of {@link #CONNECTION_PRIMARY_SERVING}, {@link #CONNECTION_SECONDARY_SERVING}.
      */
+    @ConnectionStatus
     private int mCellConnectionStatus;
 
     /**
@@ -57,15 +62,33 @@
      */
     private int mCellBandwidthDownlinkKhz;
 
-    public PhysicalChannelConfig(int status, int bandwidth) {
-        mCellConnectionStatus = status;
-        mCellBandwidthDownlinkKhz = bandwidth;
-    }
+    /**
+     * The radio technology for this physical channel.
+     */
+    @NetworkType
+    private int mRat;
 
-    public PhysicalChannelConfig(Parcel in) {
-        mCellConnectionStatus = in.readInt();
-        mCellBandwidthDownlinkKhz = in.readInt();
-    }
+    /**
+     * The rough frequency range for this physical channel.
+     */
+    @ServiceState.FrequencyRange
+    private int mFrequencyRange;
+
+    /**
+     * The absolute radio frequency channel number, {@link Integer#MAX_VALUE} if unknown.
+     */
+    private int mChannelNumber;
+
+    /**
+     * A list of data calls mapped to this physical channel. An empty list means the physical
+     * channel has no data call mapped to it.
+     */
+    private int[] mContextIds;
+
+    /**
+     * The physical cell identifier for this cell - PCI, PSC, {@link Integer#MAX_VALUE} if known.
+     */
+    private int mPhysicalCellId;
 
     @Override
     public int describeContents() {
@@ -76,6 +99,11 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mCellConnectionStatus);
         dest.writeInt(mCellBandwidthDownlinkKhz);
+        dest.writeInt(mRat);
+        dest.writeInt(mChannelNumber);
+        dest.writeInt(mFrequencyRange);
+        dest.writeIntArray(mContextIds);
+        dest.writeInt(mPhysicalCellId);
     }
 
     /**
@@ -86,6 +114,60 @@
     }
 
     /**
+     * Get the list of data call ids mapped to this physical channel. This list is sorted into
+     * ascending numerical order. Each id in this list must match the id in
+     * {@link com.android.internal.telephony.dataconnection.DataConnection}. An empty list means the
+     * physical channel has no data call mapped to it.
+     *
+     * @return an integer list indicates the data call ids.
+     */
+    public int[] getContextIds() {
+        return mContextIds;
+    }
+
+    /**
+     * @return the rough frequency range for this physical channel.
+     * @see {@link ServiceState#FREQUENCY_RANGE_LOW}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MID}
+     * @see {@link ServiceState#FREQUENCY_RANGE_HIGH}
+     * @see {@link ServiceState#FREQUENCY_RANGE_MMWAVE}
+     */
+    @ServiceState.FrequencyRange
+    public int getFrequencyRange() {
+        return mFrequencyRange;
+    }
+
+    /**
+     * @return the absolute radio frequency channel number for this physical channel,
+     * {@link Integer#MAX_VALUE} if unknown.
+     */
+    public int getChannelNumber() {
+        return mChannelNumber;
+    }
+
+    /**
+     * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+     * Reference: 3GPP TS 25.213 section 5.2.2.
+     *
+     * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+     * Reference: 3GPP TS 36.211 section 6.11.
+     *
+     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1008].
+     * Reference: 3GPP TS 38.211 section 7.4.2.1.
+     *
+     * @return the physical cell identifier for this cell, {@link Integer#MAX_VALUE} if unknown.
+     */
+    public int getPhysicalCellId() {
+        return mPhysicalCellId;
+    }
+
+    /**The radio technology for this physical channel. */
+    @NetworkType
+    public int getRat() {
+        return mRat;
+    }
+
+    /**
      * Gets the connection status of the cell.
      *
      * @see #CONNECTION_PRIMARY_SERVING
@@ -125,12 +207,19 @@
 
         PhysicalChannelConfig config = (PhysicalChannelConfig) o;
         return mCellConnectionStatus == config.mCellConnectionStatus
-                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz;
+                && mCellBandwidthDownlinkKhz == config.mCellBandwidthDownlinkKhz
+                && mRat == config.mRat
+                && mFrequencyRange == config.mFrequencyRange
+                && mChannelNumber == config.mChannelNumber
+                && mPhysicalCellId == config.mPhysicalCellId
+                && Arrays.equals(mContextIds, config.mContextIds);
     }
 
     @Override
     public int hashCode() {
-        return (mCellBandwidthDownlinkKhz * 29) + (mCellConnectionStatus * 31);
+        return Objects.hash(
+                mCellConnectionStatus, mCellBandwidthDownlinkKhz, mRat, mFrequencyRange,
+                mChannelNumber, mPhysicalCellId, mContextIds);
     }
 
     public static final Parcelable.Creator<PhysicalChannelConfig> CREATOR =
@@ -147,11 +236,111 @@
     @Override
     public String toString() {
         return new StringBuilder()
-            .append("{mConnectionStatus=")
-            .append(getConnectionStatusString())
-            .append(",mCellBandwidthDownlinkKhz=")
-            .append(mCellBandwidthDownlinkKhz)
-            .append("}")
-            .toString();
+                .append("{mConnectionStatus=")
+                .append(getConnectionStatusString())
+                .append(",mCellBandwidthDownlinkKhz=")
+                .append(mCellBandwidthDownlinkKhz)
+                .append(",mRat=")
+                .append(mRat)
+                .append(",mFrequencyRange=")
+                .append(mFrequencyRange)
+                .append(",mChannelNumber=")
+                .append(mChannelNumber)
+                .append(",mContextIds=")
+                .append(mContextIds.toString())
+                .append(",mPhysicalCellId=")
+                .append(mPhysicalCellId)
+                .append("}")
+                .toString();
+    }
+
+    private PhysicalChannelConfig(Parcel in) {
+        mCellConnectionStatus = in.readInt();
+        mCellBandwidthDownlinkKhz = in.readInt();
+        mRat = in.readInt();
+        mChannelNumber = in.readInt();
+        mFrequencyRange = in.readInt();
+        mContextIds = in.createIntArray();
+        mPhysicalCellId = in.readInt();
+    }
+
+    private PhysicalChannelConfig(Builder builder) {
+        mCellConnectionStatus = builder.mCellConnectionStatus;
+        mCellBandwidthDownlinkKhz = builder.mCellBandwidthDownlinkKhz;
+        mRat = builder.mRat;
+        mChannelNumber = builder.mChannelNumber;
+        mFrequencyRange = builder.mFrequencyRange;
+        mContextIds = builder.mContextIds;
+        mPhysicalCellId = builder.mPhysicalCellId;
+    }
+
+    /** The builder of {@code PhysicalChannelConfig}. */
+    public static final class Builder {
+        private int mRat;
+        private int mFrequencyRange;
+        private int mChannelNumber;
+        private int mCellBandwidthDownlinkKhz;
+        private int mCellConnectionStatus;
+        private int[] mContextIds;
+        private int mPhysicalCellId;
+
+        /** @hide */
+        public Builder() {
+            mRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
+            mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
+            mChannelNumber = Integer.MAX_VALUE;
+            mCellBandwidthDownlinkKhz = 0;
+            mCellConnectionStatus = CONNECTION_UNKNOWN;
+            mContextIds = new int[0];
+            mPhysicalCellId = Integer.MAX_VALUE;
+        }
+
+        /** @hide */
+        public PhysicalChannelConfig build() {
+            return new PhysicalChannelConfig(this);
+        }
+
+        /** @hide */
+        public Builder setRat(int rat) {
+            this.mRat = rat;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setFrequencyRange(int frequencyRange) {
+            this.mFrequencyRange = frequencyRange;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setChannelNumber(int channelNumber) {
+            this.mChannelNumber = channelNumber;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) {
+            this.mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setCellConnectionStatus(int connectionStatus) {
+            this.mCellConnectionStatus = connectionStatus;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setContextIds(int[] contextIds) {
+            if (contextIds != null) Arrays.sort(contextIds);
+            this.mContextIds = contextIds;
+            return this;
+        }
+
+        /** @hide */
+        public Builder setPhysicalCellId(int physicalCellId) {
+            this.mPhysicalCellId = physicalCellId;
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index da3acc2..4482074 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -460,6 +460,9 @@
         if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
             networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
         }
+        if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) {
+            networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+        }
 
         return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 777d219..ca0c854 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -118,6 +118,13 @@
      */
     public static final int FREQUENCY_RANGE_MMWAVE = 4;
 
+    private static final List<Integer> FREQUENCY_RANGE_ORDER = Arrays.asList(
+            FREQUENCY_RANGE_UNKNOWN,
+            FREQUENCY_RANGE_LOW,
+            FREQUENCY_RANGE_MID,
+            FREQUENCY_RANGE_HIGH,
+            FREQUENCY_RANGE_MMWAVE);
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "DUPLEX_MODE_",
@@ -223,9 +230,15 @@
     public static final int RIL_RADIO_TECHNOLOGY_LTE_CA = 19;
 
     /**
-     * Number of radio technologies for GSM, UMTS and CDMA.
+     * NR(New Radio) 5G.
+     * @hide
      */
-    private static final int NEXT_RIL_RADIO_TECHNOLOGY = 20;
+    public static final int  RIL_RADIO_TECHNOLOGY_NR = 20;
+
+    /**
+     * The number of the radio technologies.
+     */
+    private static final int NEXT_RIL_RADIO_TECHNOLOGY = 21;
 
     /** @hide */
     public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
@@ -1829,4 +1842,13 @@
             mNetworkRegistrationStates.add(regState);
         }
     }
+
+    /**
+     * @hide
+     */
+    public static final int getBetterNRFrequencyRange(int range1, int range2) {
+        return FREQUENCY_RANGE_ORDER.indexOf(range1) > FREQUENCY_RANGE_ORDER.indexOf(range2)
+                ? range1
+                : range2;
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index b41e14e..bacfe61a 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -150,6 +150,11 @@
     private String mGroupUUID;
 
     /**
+     *  A property in opportunistic subscription to indicate whether it is metered or not.
+     */
+    private boolean mIsMetered;
+
+    /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
@@ -158,7 +163,7 @@
             @Nullable UiccAccessRule[] accessRules, String cardId) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
                 roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardId,
-                false, null);
+                false, null, true);
     }
 
     /**
@@ -168,7 +173,7 @@
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
             @Nullable UiccAccessRule[] accessRules, String cardId, boolean isOpportunistic,
-            @Nullable String groupUUID) {
+            @Nullable String groupUUID, boolean isMetered) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -187,8 +192,10 @@
         this.mCardId = cardId;
         this.mIsOpportunistic = isOpportunistic;
         this.mGroupUUID = groupUUID;
+        this.mIsMetered = isMetered;
     }
 
+
     /**
      * @return the subscription ID.
      */
@@ -403,6 +410,18 @@
     }
 
     /**
+     * Used in opportunistic subscription ({@link #isOpportunistic()}) to indicate whether it's
+     * metered or not.This is one of the factors when deciding to switch to the subscription.
+     * (a non-metered subscription, for example, would likely be preferred over a metered one).
+     *
+     * @return whether subscription is metered.
+     * @hide
+     */
+    public boolean isMetered() {
+        return mIsMetered;
+    }
+
+    /**
      * Checks whether the app with the given context is authorized to manage this subscription
      * according to its metadata. Only supported for embedded subscriptions (if {@link #isEmbedded}
      * returns true).
@@ -496,10 +515,11 @@
             String cardId = source.readString();
             boolean isOpportunistic = source.readBoolean();
             String groupUUID = source.readString();
+            boolean isMetered = source.readBoolean();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID);
+                    isEmbedded, accessRules, cardId, isOpportunistic, groupUUID, isMetered);
         }
 
         @Override
@@ -528,6 +548,7 @@
         dest.writeString(mCardId);
         dest.writeBoolean(mIsOpportunistic);
         dest.writeString(mGroupUUID);
+        dest.writeBoolean(mIsMetered);
     }
 
     @Override
@@ -561,14 +582,14 @@
                 + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
                 + " accessRules " + Arrays.toString(mAccessRules)
                 + " cardId=" + cardIdToPrint + " isOpportunistic " + mIsOpportunistic
-                + " mGroupUUID=" + mGroupUUID + "}";
+                + " mGroupUUID=" + mGroupUUID + " isMetered=" + mIsMetered + "}";
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
-                mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso,
-                mCardId, mDisplayName, mCarrierName, mAccessRules);
+                mIsOpportunistic, mGroupUUID, mIsMetered, mIccId, mNumber, mMcc, mMnc,
+                mCountryIso, mCardId, mDisplayName, mCarrierName, mAccessRules);
     }
 
     @Override
@@ -591,6 +612,7 @@
                 && mIsEmbedded == toCompare.mIsEmbedded
                 && mIsOpportunistic == toCompare.mIsOpportunistic
                 && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
+                && mIsMetered == toCompare.mIsMetered
                 && Objects.equals(mIccId, toCompare.mIccId)
                 && Objects.equals(mNumber, toCompare.mNumber)
                 && Objects.equals(mMcc, toCompare.mMcc)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 3200aea..2a01ac4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -147,7 +147,8 @@
     public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc");
 
     /**
-     * A content {@link Uri} used to receive updates on advanced calling user setting.
+     * A content {@link Uri} used to receive updates on advanced calling user setting
+     * @see ImsMmTelManager#isAdvancedCallingSettingEnabled().
      * <p>
      * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
      * subscription advanced calling enabled
@@ -406,6 +407,13 @@
     public static final String MNC = "mnc";
 
     /**
+     * TelephonyProvider column name for the iso country code associated with a SIM.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+    public static final String ISO_COUNTRY_CODE = "iso_country_code";
+
+    /**
      * TelephonyProvider column name for the sim provisioning status associated with a SIM.
      * <P>Type: INTEGER (int)</P>
      * @hide
@@ -568,14 +576,6 @@
     public static final String IS_OPPORTUNISTIC = "is_opportunistic";
 
     /**
-     * TelephonyProvider column name for subId of parent subscription of an opportunistic
-     * subscription.
-     * if the parent sub id is valid, then is_opportunistic should always to true.
-     * @hide
-     */
-    public static final String PARENT_SUB_ID = "parent_sub_id";
-
-    /**
      * TelephonyProvider column name for group ID. Subscriptions with same group ID
      * are considered bundled together, and should behave as a single subscription at
      * certain scenarios.
@@ -583,7 +583,12 @@
      * @hide
      */
     public static final String GROUP_UUID = "group_uuid";
-
+    /**
+     * TelephonyProvider column name for whether a subscription is metered or not, that is, whether
+     * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid.
+     * @hide
+     */
+    public static final String IS_METERED = "is_metered";
     /**
      * Broadcast Action: The user has changed one of the default subs related to
      * data, phone calls, or sms</p>
@@ -1426,7 +1431,7 @@
      * subscriptions in the slot.
      */
     @Nullable
-    public static int[] getSubscriptionIds(int slotIndex) {
+    public int[] getSubscriptionIds(int slotIndex) {
         return getSubId(slotIndex);
     }
 
@@ -2410,6 +2415,21 @@
         return groupUUID;
     }
 
+    /**
+     * Set metered by simInfo index
+     *
+     * @param isMetered whether it’s a metered subscription.
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int setMetered(boolean isMetered, int subId) {
+        if (VDBG) logd("[setIsMetered]+ isMetered:" + isMetered + " subId:" + subId);
+        return setSubscriptionPropertyHelper(subId, "setIsMetered",
+                (iSub)-> iSub.setMetered(isMetered, subId));
+    }
+
     private interface CallISubMethodHelper {
         int callMethod(ISub iSub) throws RemoteException;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 79ed93e..45d914e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -77,6 +77,7 @@
 import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IAns;
+import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -1341,6 +1342,13 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
+    /**
+     * The max value for the timeout passed in {@link #requestNumberVerification}.
+     * @hide
+     */
+    @SystemApi
+    public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+
     //
     //
     // Device Info
@@ -2243,9 +2251,11 @@
     /** Current network is LTE_CA {@hide} */
     @UnsupportedAppUsage
     public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
+    /** Current network is NR(New Radio) 5G. */
+    public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
 
     /** Max network type number. Update as new types are added. Don't add negative types. {@hide} */
-    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_LTE_CA;
+    public static final int MAX_NETWORK_TYPE = NETWORK_TYPE_NR;
 
     /** @hide */
     @IntDef({
@@ -2269,6 +2279,7 @@
             NETWORK_TYPE_TD_SCDMA,
             NETWORK_TYPE_IWLAN,
             NETWORK_TYPE_LTE_CA,
+            NETWORK_TYPE_NR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType{}
@@ -4214,7 +4225,8 @@
     }
 
     /**
-     * Returns the voice mail count for a subscription. Return 0 if unavailable.
+     * Returns the voice mail count for a subscription. Return 0 if unavailable or the caller does
+     * not have the READ_PHONE_STATE permission.
      * @param subId whose voice message count is returned
      * @hide
      */
@@ -4225,7 +4237,7 @@
             ITelephony telephony = getITelephony();
             if (telephony == null)
                 return 0;
-            return telephony.getVoiceMessageCountForSubscriber(subId);
+            return telephony.getVoiceMessageCountForSubscriber(subId, getOpPackageName());
         } catch (RemoteException ex) {
             return 0;
         } catch (NullPointerException ex) {
@@ -5368,7 +5380,7 @@
 
     /**
      * Rollback modem configurations to factory default except some config which are in whitelist.
-     * Used for device configuration by some CDMA operators.
+     * Used for device configuration by some carriers.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5395,7 +5407,7 @@
     }
 
     /**
-     * Generate a radio modem reset. Used for device configuration by some CDMA operators.
+     * Generate a radio modem reset. Used for device configuration by some carriers.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5499,6 +5511,52 @@
     }
 
     /**
+     * Request that the next incoming call from a number matching {@code range} be intercepted.
+     *
+     * This API is intended for OEMs to provide a service for apps to verify the device's phone
+     * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and
+     * intercept the next incoming call from a number that lies within the range, within a timeout
+     * specified by {@code timeoutMillis}.
+     *
+     * If such a phone call is received, the caller will be notified via
+     * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}.
+     * If verification fails for any reason, the caller will be notified via
+     * {@link NumberVerificationCallback#onVerificationFailed(int)}
+     * on the provided {@link Executor}.
+     *
+     * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
+     * API must also be listed in the device configuration as an authorized app in
+     * {@code packages/services/Telephony/res/values/config.xml} under the
+     * {@code config_number_verification_package_name} key.
+     *
+     * @hide
+     * @param range The range of phone numbers the caller expects a phone call from.
+     * @param timeoutMillis The amount of time to wait for such a call, or
+     *                      {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+     * @param executor The {@link Executor} that callbacks should be executed on.
+     * @param callback The callback to use for delivering results.
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull NumberVerificationCallback callback) {
+        INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
+            @Override
+            public void onCallReceived(String phoneNumber) throws RemoteException {
+                Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber));
+            }
+
+            @Override
+            public void onVerificationFailed(int reason) throws RemoteException {
+                Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason));
+            }
+        };
+
+        // TODO -- call the aidl method
+    }
+
+    /**
      * Sets a per-phone telephony property with the value specified.
      *
      * @hide
@@ -8579,7 +8637,8 @@
 
     /**
      * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
-     * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+     * All uicc applications are uniquely identified by application ID, represented by the hex
+     * string. e.g, A00000015141434C00. See ETSI 102.221 and 101.220
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
@@ -9216,6 +9275,7 @@
                     NETWORK_TYPE_BITMASK_TD_SCDMA,
                     NETWORK_TYPE_BITMASK_LTE,
                     NETWORK_TYPE_BITMASK_LTE_CA,
+                    NETWORK_TYPE_BITMASK_NR,
             })
     public @interface NetworkTypeBitMask {}
 
@@ -9332,6 +9392,13 @@
     public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
 
     /**
+     * network type bitmask indicating the support of radio tech NR(New Radio) 5G.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR);
+
+    /**
      * @return Modem supported radio access family bitmask
      *
      * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
@@ -9520,4 +9587,34 @@
         }
         return subId;
     }
+
+    /**
+     * Update availability of a list of networks in the current location.
+     *
+     * This api should be called to inform AlternativeNetwork Service about the availability
+     * of a network at the current location. This information will be used by AlternativeNetwork
+     * service to decide to attach to the network opportunistically. If an empty list is passed,
+     * it is assumed that no network is available.
+     * Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * @param availableNetworks is a list of available network information.
+     * @return true if request is accepted
+     *
+     */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    public boolean updateAvailableNetworks(List<AvailableNetworkInfo> availableNetworks) {
+        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        boolean ret = false;
+        try {
+            IAns iAlternativeNetworkService = getIAns();
+            if (iAlternativeNetworkService != null) {
+                ret = iAlternativeNetworkService.updateAvailableNetworks(availableNetworks,
+                        pkgForDebug);
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
+        }
+        return ret;
+    }
 }
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 2d46ec2..41f7bd7 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -17,6 +17,7 @@
 package android.telephony.emergency;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.hardware.radio.V1_4.EmergencyNumberSource;
 import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
@@ -196,7 +197,7 @@
     private final int mEmergencyNumberSourceBitmask;
 
     /** @hide */
-    public EmergencyNumber(String number, String countryIso,
+    public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
                            int emergencyServiceCategories,
                            int emergencyNumberSources) {
         this.mNumber = number;
@@ -403,7 +404,7 @@
      *         0 if both have equal display priority.
      */
     @Override
-    public int compareTo(EmergencyNumber emergencyNumber) {
+    public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
         if (this.getDisplayPriorityScore()
                 > emergencyNumber.getDisplayPriorityScore()) {
             return -1;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index f73036e..a6c24bf 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -23,6 +23,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telecom.VideoProfile;
+import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.util.Log;
 
 import com.android.internal.telephony.PhoneConstants;
@@ -295,6 +297,28 @@
     public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
 
     /**
+     * The emergency service categories, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    private @EmergencyServiceCategories int mEmergencyServiceCategories =
+            EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+
+    /**
      * Extras associated with this {@link ImsCallProfile}.
      * <p>
      * Valid data types include:
@@ -495,6 +519,7 @@
         out.writeInt(mCallType);
         out.writeBundle(filteredExtras);
         out.writeParcelable(mMediaProfile, 0);
+        out.writeInt(mEmergencyServiceCategories);
     }
 
     private void readFromParcel(Parcel in) {
@@ -502,6 +527,7 @@
         mCallType = in.readInt();
         mCallExtras = in.readBundle();
         mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
+        mEmergencyServiceCategories = in.readInt();
     }
 
     public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -710,4 +736,53 @@
     private static boolean isVideoStateSet(int videoState, int videoStateToCheck) {
         return (videoState & videoStateToCheck) == videoStateToCheck;
     }
+
+    /**
+     * Set the emergency service categories. The set value is valid only if
+     * {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    public void setEmergencyServiceCategories(
+            @EmergencyServiceCategories int emergencyServiceCategories) {
+        mEmergencyServiceCategories = emergencyServiceCategories;
+    }
+
+    /**
+     * Get the emergency service categories, only valid if {@link #getServiceType} returns
+     * {@link #SERVICE_TYPE_EMERGENCY}
+     *
+     * @return the emergency service categories,
+     *
+     * If valid, the value is the bitwise-OR combination of the following constants:
+     * <ol>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_POLICE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AMBULANCE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_MIEC} </li>
+     * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
+     * </ol>
+     *
+     * Reference: 3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 22.101, Section 10 - Emergency Calls.
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategories() {
+        return mEmergencyServiceCategories;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IAns.aidl b/telephony/java/com/android/internal/telephony/IAns.aidl
index e9a4649..98bcd41 100755
--- a/telephony/java/com/android/internal/telephony/IAns.aidl
+++ b/telephony/java/com/android/internal/telephony/IAns.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.telephony.AvailableNetworkInfo;
 
 interface IAns {
 
@@ -78,4 +79,23 @@
      *
      */
     int getPreferredData(String callingPackage);
+
+    /**
+     * Update availability of a list of networks in the current location.
+     *
+     * This api should be called if the caller is aware of the availability of a network
+     * at the current location. This information will be used by AlternativeNetwork service
+     * to decide to attach to the network. If an empty list is passed,
+     * it is assumed that no network is available.
+     * Requires that the calling app has carrier privileges on both primary and
+     * secondary subscriptions (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     *  @param availableNetworks is a list of available network information.
+     *  @param callingPackage caller's package name
+     *  @return true if request is accepted
+     *
+     */
+    boolean updateAvailableNetworks(in List<AvailableNetworkInfo> availableNetworks,
+            String callingPackage);
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
index 27d25b8..76918af 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
@@ -13,12 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.internal.telephony;
+
+oneway interface INumberVerificationCallback {
+    void onCallReceived(String phoneNumber);
+    void onVerificationFailed(int reason);
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index bc44519..f9db4b0 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -184,6 +184,15 @@
     String setSubscriptionGroup(in int[] subIdList, String callingPackage);
 
     /**
+     * Set whether a subscription is metered
+     *
+     * @param isMetered whether it’s a metered subscription.
+     * @param subId the unique SubscriptionInfo index in database
+     * @return the number of records updated
+     */
+    int setMetered(boolean isMetered, int subId);
+
+    /**
      * Set which subscription is preferred for cellular data. It's
      * designed to overwrite default data subscription temporarily.
      *
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index fc42de5..32e939a0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -393,16 +393,11 @@
     int getDataActivationState(int subId, String callingPackage);
 
     /**
-      * Returns the unread count of voicemails
-      */
-    int getVoiceMessageCount();
-
-    /**
      * Returns the unread count of voicemails for a subId.
      * @param subId user preferred subId.
      * Returns the unread count of voicemails
      */
-    int getVoiceMessageCountForSubscriber(int subId);
+    int getVoiceMessageCountForSubscriber(int subId, String callingPackage);
 
     /**
       * Returns true if current state supports both voice and data
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 923ab06..76e7509 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -25,6 +25,7 @@
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.emergency.EmergencyNumber;
 import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
 
@@ -80,4 +81,5 @@
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyPreferredDataSubIdChanged(int preferredSubId);
     void notifyRadioPowerStateChanged(in int state);
+    void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
 }
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 7842a1c..5b58dd5 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -80,6 +80,7 @@
     method public java.io.File getNoBackupFilesDir();
     method public java.io.File getObbDir();
     method public java.io.File[] getObbDirs();
+    method public java.lang.String getOpPackageName();
     method public java.lang.String getPackageCodePath();
     method public android.content.pm.PackageManager getPackageManager();
     method public java.lang.String getPackageName();
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index f1ec000..8b2c815 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -1,9 +1,5 @@
 package android.test.mock {
 
-  public class MockContext extends android.content.Context {
-    method public java.lang.String getOpPackageName();
-  }
-
   public deprecated class MockPackageManager extends android.content.pm.PackageManager {
     method public boolean arePermissionsIndividuallyControlled();
     method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 8d8fc84..b9e282e 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -38,7 +38,7 @@
             public Engine onCreateEngine() {
                 return new Engine() {
                     @Override
-                    public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
+                    public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
                         ambientModeChangedCount[0]++;
                     }
                 };
@@ -47,12 +47,12 @@
         WallpaperService.Engine engine = service.onCreateEngine();
         engine.setCreated(true);
 
-        engine.doAmbientModeChanged(false, false);
+        engine.doAmbientModeChanged(false, 0);
         assertFalse("ambient mode should be false", engine.isInAmbientMode());
         assertEquals("onAmbientModeChanged should have been called",
                 ambientModeChangedCount[0], 1);
 
-        engine.doAmbientModeChanged(true, false);
+        engine.doAmbientModeChanged(true, 0);
         assertTrue("ambient mode should be false", engine.isInAmbientMode());
         assertEquals("onAmbientModeChanged should have been called",
                 ambientModeChangedCount[0], 2);
diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore
index bd79078..7f4121a 100644
--- a/tests/TouchLatency/.gitignore
+++ b/tests/TouchLatency/.gitignore
@@ -3,4 +3,5 @@
 /.idea
 .DS_Store
 /build
+/gen
 .iml
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 2337110..2594322 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 21
-    buildToolsVersion "21.1.2"
+    compileSdkVersion 28
+    buildToolsVersion '28.0.3'
 
     defaultConfig {
         applicationId "com.prefabulated.touchlatency"
         minSdkVersion 21
-        targetSdkVersion 21
+        targetSdkVersion 28
         versionCode 1
         versionName "1.0"
     }
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index b4b5ca7..360c22f 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -19,11 +19,9 @@
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
-import android.os.CountDownTimer;
+import android.graphics.Paint.Align;
 import android.os.Bundle;
-import android.text.method.Touch;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Menu;
@@ -31,15 +29,17 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.os.Trace;
-
-import java.util.ArrayList;
-import java.util.Collections;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
 
 class TouchLatencyView extends View implements View.OnTouchListener {
     private static final String LOG_TAG = "TouchLatency";
     private static final int BACKGROUND_COLOR = 0xFF400080;
     private static final int INNER_RADIUS = 70;
-    private static final int BALL_RADIUS = 100;
+    private static final int BALL_DIAMETER = 200;
+    private static final int SEC_TO_NANOS = 1000000000;
+    private static final float FPS_UPDATE_THRESHOLD = 20;
+    private static final long BALL_VELOCITY = 420;
 
     public TouchLatencyView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,13 +58,17 @@
         mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mRedPaint.setColor(0xFFFF0000);
         mRedPaint.setStyle(Paint.Style.FILL);
+        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mTextPaint.setColor(0xFFFFFFFF);
+        mTextPaint.setTextSize(100);
+        mTextPaint.setTextAlign(Align.RIGHT);
 
         mTouching = false;
 
-        mBallX = 100.0f;
-        mBallY = 100.0f;
-        mVelocityX = 7.0f;
-        mVelocityY = 7.0f;
+        mLastDrawNano = 0;
+        mFps = 0;
+        mLastFpsUpdate = 0;
+        mFrameCount = 0;
         Trace.endSection();
     }
 
@@ -113,43 +117,70 @@
         }
     }
 
+    private Paint getBallColor() {
+        if (mFps > 75)
+            return mGreenPaint;
+        else if (mFps > 45)
+            return mYellowPaint;
+        else
+            return mRedPaint;
+    }
+
     private void drawBall(Canvas canvas) {
         Trace.beginSection("TouchLatencyView drawBall");
         int width = canvas.getWidth();
         int height = canvas.getHeight();
+        float fps = 0f;
 
-        // Update position
-        mBallX += mVelocityX;
-        mBallY += mVelocityY;
+        long t = System.nanoTime();
+        long tDiff = t - mLastDrawNano;
+        mLastDrawNano = t;
+        mFrameCount++;
 
-        // Clamp and change velocity if necessary
-        float left = mBallX - BALL_RADIUS;
-        if (left < 0) {
-            left = 0;
-            mVelocityX *= -1;
+        if (tDiff < SEC_TO_NANOS) {
+            fps = 1f * SEC_TO_NANOS / tDiff;
         }
 
-        float top = mBallY - BALL_RADIUS;
-        if (top < 0) {
-            top = 0;
-            mVelocityY *= -1;
+        long fDiff = t - mLastFpsUpdate;
+        if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+            mFps = fps;
+            mLastFpsUpdate = t;
+            mFrameCount = 0;
+        } else if (fDiff > SEC_TO_NANOS) {
+            mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+            mLastFpsUpdate = t;
+            mFrameCount = 0;
         }
 
-        float right = mBallX + BALL_RADIUS;
-        if (right > width) {
-            right = width;
-            mVelocityX *= -1;
-        }
+        final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+        final long xMax = width - BALL_DIAMETER;
+        final long yMax = height - BALL_DIAMETER;
+        long xOffset = pos % xMax;
+        long yOffset = pos % yMax;
 
-        float bottom = mBallY + BALL_RADIUS;
-        if (bottom > height) {
-            bottom = height;
-            mVelocityY *= -1;
+        float left, right, top, bottom;
+
+        if (((pos / xMax) & 1) == 0) {
+            left = xMax - xOffset;
+        } else {
+            left = xOffset;
         }
+        right = left + BALL_DIAMETER;
+
+        if (((pos / yMax) & 1) == 0) {
+            top = yMax - yOffset;
+        } else {
+            top = yOffset;
+        }
+        bottom = top + BALL_DIAMETER;
 
         // Draw the ball
         canvas.drawColor(BACKGROUND_COLOR);
-        canvas.drawOval(left, top, right, bottom, mYellowPaint);
+        canvas.drawOval(left, top, right, bottom, getBallColor());
+        DecimalFormat df = new DecimalFormat("fps: #.##");
+        df.setRoundingMode(RoundingMode.HALF_UP);
+        canvas.drawText(df.format(mFps), width, 100, mTextPaint);
+
         invalidate();
         Trace.endSection();
     }
@@ -176,15 +207,15 @@
         Trace.endSection();
     }
 
-    private Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint;
+    private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
     private int mMode;
 
     private boolean mTouching;
     private float mTouchX, mTouchY;
     private float mLastDrawnX, mLastDrawnY;
 
-    private float mBallX, mBallY;
-    private float mVelocityX, mVelocityY;
+    private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+    private float mFps;
 }
 
 public class TouchLatencyActivity extends Activity {
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index d3ff69d..03abe82 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -3,9 +3,10 @@
 buildscript {
     repositories {
         jcenter()
+        google()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.1.0'
+        classpath 'com.android.tools.build:gradle:3.2.1'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -15,5 +16,6 @@
 allprojects {
     repositories {
         jcenter()
+        google()
     }
 }
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 0c71e76..111992a 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Nov 27 13:37:59 PST 2018
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index ae3914e..d5987a5 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -26,6 +26,7 @@
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.IWindowSession;
+import android.view.InsetsState;
 import android.view.Surface;
 import android.view.View;
 import android.view.WindowManager;
@@ -105,7 +106,7 @@
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
-                                new Surface());
+                                new Surface(), new InsetsState());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
@@ -131,8 +132,9 @@
             final IWindowSession session = WindowManagerGlobal.getWindowSession();
             final Rect tmpRect = new Rect();
             try {
-                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq, layoutParams,
-                        View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect);
+                final int res = session.addToDisplayWithoutInputChannel(window, window.mSeq,
+                        layoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, tmpRect, tmpRect,
+                        new InsetsState());
             } catch (RemoteException e) {
                 e.printStackTrace();
             }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4dc0341..d8f9618 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -542,7 +542,7 @@
 
         verify(mMockNetd)
                 .ipSecApplyTransportModeTransform(
-                        eq(pfd.getFileDescriptor()),
+                        eq(pfd),
                         eq(mUid),
                         eq(IpSecManager.DIRECTION_OUT),
                         anyString(),
@@ -555,7 +555,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     private IpSecTunnelInterfaceResponse createAndValidateTunnel(
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 2c94a60..724446e 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -425,7 +425,7 @@
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
         mIpSecService.removeTransportModeTransforms(pfd);
 
-        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
+        verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd);
     }
 
     @Test
@@ -620,10 +620,10 @@
                 mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor();
-        ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> {
+        ArgumentMatcher<ParcelFileDescriptor> fdMatcher = (arg) -> {
                     try {
                         StructStat sockStat = Os.fstat(sockFd);
-                        StructStat argStat = Os.fstat(arg);
+                        StructStat argStat = Os.fstat(arg.getFileDescriptor());
 
                         return sockStat.st_ino == argStat.st_ino
                                 && sockStat.st_dev == argStat.st_dev;
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
index b399b0d..6e07b26 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -40,6 +40,7 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -70,6 +71,7 @@
     private @Mock Handler mHandler;
     private @Mock IpConnectivityLog mLogger;
     private @Mock NetworkAgentInfo mAgent;
+    private @Mock NetworkAgentInfo mNotMeteredAgent;
     private @Mock NetworkInfo mNetworkInfo;
     private @Mock NetworkRequest mRequest;
     private @Mock TelephonyManager mTelephony;
@@ -87,6 +89,10 @@
     private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
     private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
 
+    private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
+    private static final int RETURN_CODE_DNS_SUCCESS = 0;
+    private static final int RETURN_CODE_DNS_TIMEOUT = 255;
+
     @Before
     public void setUp() throws IOException {
         MockitoAnnotations.initMocks(this);
@@ -95,6 +101,12 @@
                 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
         mAgent.networkInfo = mNetworkInfo;
 
+        mNotMeteredAgent.linkProperties = new LinkProperties();
+        mNotMeteredAgent.networkCapabilities = new NetworkCapabilities()
+            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        mNotMeteredAgent.networkInfo = mNetworkInfo;
+
         when(mAgent.network()).thenReturn(mNetwork);
         when(mDependencies.getNetwork(any())).thenReturn(mNetwork);
         when(mDependencies.getRandom()).thenReturn(mRandom);
@@ -138,6 +150,40 @@
         when(mNetwork.getAllByName(any())).thenReturn(new InetAddress[] {
             InetAddress.parseNumericAddress("192.168.0.0")
         });
+
+        setMinDataStallEvaluateInterval(500);
+        setDataStallEvaluationType(1 << DATA_STALL_EVALUATION_TYPE_DNS);
+        setValidDataStallDnsTimeThreshold(500);
+        setConsecutiveDnsTimeoutThreshold(5);
+    }
+
+    private class WrappedNetworkMonitor extends NetworkMonitor {
+        private long mProbeTime = 0;
+
+        WrappedNetworkMonitor(Context context, Handler handler,
+                NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
+                IpConnectivityLog logger, Dependencies deps) {
+                super(context, handler, networkAgentInfo, defaultRequest, logger, deps);
+        }
+
+        @Override
+        protected long getLastProbeTime() {
+            return mProbeTime;
+        }
+
+        protected void setLastProbeTime(long time) {
+            mProbeTime = time;
+        }
+    }
+
+    WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() {
+        return new WrappedNetworkMonitor(
+                mContext, mHandler, mAgent, mRequest, mLogger, mDependencies);
+    }
+
+    WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() {
+        return new WrappedNetworkMonitor(
+                mContext, mHandler, mNotMeteredAgent, mRequest, mLogger, mDependencies);
     }
 
     NetworkMonitor makeMonitor() {
@@ -272,6 +318,113 @@
         assertPortal(makeMonitor().isCaptivePortal());
     }
 
+    @Test
+    public void testIsDataStall_EvaluationDisabled() {
+        setDataStallEvaluationType(0);
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        assertFalse(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
+        WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 3);
+        assertFalse(wrappedMonitor.isDataStall());
+        // Reset consecutive timeout counts.
+        makeDnsSuccessEvent(wrappedMonitor, 1);
+        makeDnsTimeoutEvent(wrappedMonitor, 2);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        makeDnsTimeoutEvent(wrappedMonitor, 3);
+        assertTrue(wrappedMonitor.isDataStall());
+
+        // Set the value to larger than the default dns log size.
+        setConsecutiveDnsTimeoutThreshold(51);
+        wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        makeDnsTimeoutEvent(wrappedMonitor, 50);
+        assertFalse(wrappedMonitor.isDataStall());
+
+        makeDnsTimeoutEvent(wrappedMonitor, 1);
+        assertTrue(wrappedMonitor.isDataStall());
+    }
+
+    @Test
+    public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
+        // Test dns events happened in valid dns time threshold.
+        WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertFalse(wrappedMonitor.isDataStall());
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        assertTrue(wrappedMonitor.isDataStall());
+
+        // Test dns events happened before valid dns time threshold.
+        setValidDataStallDnsTimeThreshold(0);
+        wrappedMonitor = makeMeteredWrappedNetworkMonitor();
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
+        makeDnsTimeoutEvent(wrappedMonitor, 5);
+        assertFalse(wrappedMonitor.isDataStall());
+        wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
+        assertFalse(wrappedMonitor.isDataStall());
+    }
+
+    private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+        for (int i = 0; i < count; i++) {
+            wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+                    RETURN_CODE_DNS_TIMEOUT);
+        }
+    }
+
+    private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
+        for (int i = 0; i < count; i++) {
+            wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
+                    RETURN_CODE_DNS_SUCCESS);
+        }
+    }
+
+    private void setDataStallEvaluationType(int type) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
+    }
+
+    private void setMinDataStallEvaluateInterval(int time) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
+    }
+
+    private void setValidDataStallDnsTimeThreshold(int time) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
+    }
+
+    private void setConsecutiveDnsTimeoutThreshold(int num) {
+        when(mDependencies.getSetting(any(),
+            eq(Settings.Global.DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt()))
+            .thenReturn(num);
+    }
+
     private void setFallbackUrl(String url) {
         when(mDependencies.getSetting(any(),
                 eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index ed70fb3..df0daeb 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -240,6 +240,12 @@
         }
         break;
 
+      case android::RES_TABLE_OVERLAYABLE_TYPE:
+        if (!ParseOverlayable(parser.chunk())) {
+          return false;
+        }
+        break;
+
       default:
         diag_->Warn(DiagMessage(source_)
                     << "unexpected chunk type "
@@ -383,24 +389,12 @@
       return false;
     }
 
-    const uint32_t type_spec_flags = entry_type_spec_flags_[res_id];
-    if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0 ||
-        (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) != 0) {
-      if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
-        Visibility visibility;
-        visibility.level = Visibility::Level::kPublic;
-        visibility.source = source_.WithLine(0);
-        if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
-          return false;
-        }
-      }
-
-      if (type_spec_flags & ResTable_typeSpec::SPEC_OVERLAYABLE) {
-        Overlayable overlayable;
-        overlayable.source = source_.WithLine(0);
-        if (!table_->AddOverlayableMangled(name, overlayable, diag_)) {
-          return false;
-        }
+    if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
+      Visibility visibility;
+      visibility.level = Visibility::Level::kPublic;
+      visibility.source = source_.WithLine(0);
+      if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
+        return false;
       }
 
       // Erase the ID from the map once processed, so that we don't mark the same symbol more than
@@ -433,6 +427,72 @@
   return true;
 }
 
+bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
+  const ResTable_overlayable_header* header = ConvertTo<ResTable_overlayable_header>(chunk);
+  if (!header) {
+    diag_->Error(DiagMessage(source_) << "corrupt ResTable_category_header chunk");
+    return false;
+  }
+
+  ResChunkPullParser parser(GetChunkData(chunk),
+                            GetChunkDataLen(chunk));
+  while (ResChunkPullParser::IsGoodEvent(parser.Next())) {
+    if (util::DeviceToHost16(parser.chunk()->type) == android::RES_TABLE_OVERLAYABLE_POLICY_TYPE) {
+      const ResTable_overlayable_policy_header* policy_header =
+          ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
+
+      std::vector<Overlayable::Policy> policies;
+      if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
+        policies.push_back(Overlayable::Policy::kPublic);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
+        policies.push_back(Overlayable::Policy::kSystem);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
+        policies.push_back(Overlayable::Policy::kVendor);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
+        policies.push_back(Overlayable::Policy::kProduct);
+      }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION) {
+        policies.push_back(Overlayable::Policy::kProductServices);
+      }
+
+      const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
+          ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
+      const ResTable_ref* const ref_end = ref_begin
+          + util::DeviceToHost32(policy_header->entry_count);
+      for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+        ResourceId res_id(util::DeviceToHost32(ref_iter->ident));
+        const auto iter = id_index_.find(res_id);
+
+        // If the overlayable chunk comes before the type chunks, the resource ids and resource name
+        // pairing will not exist at this point.
+        if (iter == id_index_.cend()) {
+          diag_->Error(DiagMessage(source_) << "failed to find resource name for overlayable"
+                                            << " resource " << res_id);
+          return false;
+        }
+
+        for (Overlayable::Policy policy : policies) {
+          Overlayable overlayable;
+          overlayable.source = source_.WithLine(0);
+          overlayable.policy = policy;
+          if (!table_->AddOverlayable(iter->second, overlayable, diag_)) {
+            return false;
+          }
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
 std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
                                                        const ConfigDescription& config,
                                                        const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 2bdc051..a2eee50 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -54,6 +54,7 @@
   bool ParseTypeSpec(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
   bool ParseType(const ResourceTablePackage* package, const android::ResChunk_header* chunk);
   bool ParseLibrary(const android::ResChunk_header* chunk);
+  bool ParseOverlayable(const android::ResChunk_header* chunk);
 
   std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
                                    const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 6c1a9ba..976c328 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -24,6 +24,7 @@
 #include "android-base/logging.h"
 #include "android-base/macros.h"
 #include "android-base/stringprintf.h"
+#include "androidfw/ResourceUtils.h"
 
 #include "ResourceTable.h"
 #include "ResourceValues.h"
@@ -216,6 +217,11 @@
   size_t entry_count_ = 0;
 };
 
+struct PolicyChunk {
+  uint32_t policy_flags;
+  std::set<ResourceId> ids;
+};
+
 class PackageFlattener {
  public:
   PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
@@ -267,6 +273,8 @@
       FlattenLibrarySpec(buffer);
     }
 
+    FlattenOverlayable(buffer);
+
     pkg_writer.Finish();
     return true;
   }
@@ -413,6 +421,97 @@
     return sorted_entries;
   }
 
+  void FlattenOverlayable(BigBuffer* buffer) {
+    std::vector<PolicyChunk> policies;
+
+    CHECK(bool(package_->id)) << "package must have an ID set when flattening <overlayable>";
+    for (auto& type : package_->types) {
+      CHECK(bool(type->id)) << "type must have an ID set when flattening <overlayable>";
+      for (auto& entry : type->entries) {
+        CHECK(bool(type->id)) << "entry must have an ID set when flattening <overlayable>";
+
+        // TODO(b/120298168): Convert the policies vector to a policy set or bitmask
+        if (!entry->overlayable_declarations.empty()) {
+          uint16_t policy_flags = 0;
+          for (Overlayable overlayable : entry->overlayable_declarations) {
+            if (overlayable.policy) {
+              switch (overlayable.policy.value()) {
+                case Overlayable::Policy::kPublic:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+                  break;
+                case Overlayable::Policy::kSystem:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+                  break;
+                case Overlayable::Policy::kVendor:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+                  break;
+                case Overlayable::Policy::kProduct:
+                  policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+                  break;
+                case Overlayable::Policy::kProductServices:
+                  policy_flags |=
+                      ResTable_overlayable_policy_header::POLICY_PRODUCT_SERVICES_PARTITION;
+                  break;
+              }
+            } else {
+              // Encode overlayable entries defined without a policy as publicly overlayable
+              policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
+            }
+          }
+
+          // Find the overlayable policy chunk with the same policies as the entry
+          PolicyChunk* policy_chunk = nullptr;
+          for (PolicyChunk& policy : policies) {
+            if (policy.policy_flags == policy_flags) {
+              policy_chunk = &policy;
+              break;
+            }
+          }
+
+          // Create a new policy chunk if an existing one with the same policy cannot be found
+          if (policy_chunk == nullptr) {
+            PolicyChunk p;
+            p.policy_flags = policy_flags;
+            policies.push_back(p);
+            policy_chunk = &policies.back();
+          }
+
+          policy_chunk->ids.insert(android::make_resid(package_->id.value(), type->id.value(),
+                                                       entry->id.value()));
+        }
+      }
+    }
+
+    if (policies.empty()) {
+      // Only write the overlayable chunk if the APK has overlayable entries
+      return;
+    }
+
+    ChunkWriter writer(buffer);
+    writer.StartChunk<ResTable_overlayable_header>(RES_TABLE_OVERLAYABLE_TYPE);
+
+    // Write each policy block for the overlayable
+    for (PolicyChunk& policy : policies) {
+      ChunkWriter policy_writer(buffer);
+      ResTable_overlayable_policy_header* policy_type =
+          policy_writer.StartChunk<ResTable_overlayable_policy_header>(
+              RES_TABLE_OVERLAYABLE_POLICY_TYPE);
+      policy_type->policy_flags = util::HostToDevice32(policy.policy_flags);
+      policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(policy.ids.size()));
+
+      // Write the ids after the policy header
+      ResTable_ref* id_block = policy_writer.NextBlock<ResTable_ref>(policy.ids.size());
+      for (const ResourceId& id : policy.ids) {
+        id_block->ident = util::HostToDevice32(id.id);
+        id_block++;
+      }
+
+      policy_writer.Finish();
+    }
+
+    writer.Finish();
+  }
+
   bool FlattenTypeSpec(ResourceTableType* type, std::vector<ResourceEntry*>* sorted_entries,
                        BigBuffer* buffer) {
     ChunkWriter type_spec_writer(buffer);
@@ -446,11 +545,6 @@
         config_masks[entry->id.value()] |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
       }
 
-      if (!entry->overlayable_declarations.empty()) {
-        config_masks[entry->id.value()] |=
-            util::HostToDevice32(ResTable_typeSpec::SPEC_OVERLAYABLE);
-      }
-
       const size_t config_count = entry->values.size();
       for (size_t i = 0; i < config_count; i++) {
         const ConfigDescription& config = entry->values[i]->config;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index cd1414c..410efbe 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -628,24 +628,108 @@
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayable) {
+  std::string name = "com.app.test:integer/overlayable";
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
-          .AddSimple("com.app.test:integer/overlayable", ResourceId(0x7f020000))
+          .AddSimple(name, ResourceId(0x7f020000))
+          .AddOverlayable(name, Overlayable::Policy::kProduct)
+          .AddOverlayable(name, Overlayable::Policy::kSystem)
+          .AddOverlayable(name, Overlayable::Policy::kVendor)
           .Build();
 
-  ASSERT_TRUE(table->AddOverlayable(test::ParseNameOrDie("com.app.test:integer/overlayable"),
-                                    Overlayable{}, test::GetDiagnostics()));
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
 
-  ResTable res_table;
-  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
-
-  const StringPiece16 overlayable_name(u"com.app.test:integer/overlayable");
-  uint32_t spec_flags = 0u;
-  ASSERT_THAT(res_table.identifierForName(overlayable_name.data(), overlayable_name.size(), nullptr,
-                                          0u, nullptr, 0u, &spec_flags),
-              Gt(0u));
-  EXPECT_TRUE(spec_flags & android::ResTable_typeSpec::SPEC_OVERLAYABLE);
+  auto search_result = output_table.FindResource(test::ParseNameOrDie(name));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kVendor);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProduct);
 }
 
+TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  std::string name_one = "com.app.test:integer/overlayable_one";
+  std::string name_two = "com.app.test:integer/overlayable_two";
+  std::string name_three = "com.app.test:integer/overlayable_three";
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .AddOverlayable(name_zero, Overlayable::Policy::kProduct)
+          .AddOverlayable(name_zero, Overlayable::Policy::kSystem)
+          .AddOverlayable(name_zero, Overlayable::Policy::kProductServices)
+          .AddSimple(name_one, ResourceId(0x7f020001))
+          .AddOverlayable(name_one, Overlayable::Policy::kPublic)
+          .AddOverlayable(name_one, Overlayable::Policy::kSystem)
+          .AddSimple(name_two, ResourceId(0x7f020002))
+          .AddOverlayable(name_two, Overlayable::Policy::kProduct)
+          .AddOverlayable(name_two, Overlayable::Policy::kSystem)
+          .AddOverlayable(name_two, Overlayable::Policy::kProductServices)
+          .AddSimple(name_three, ResourceId(0x7f020003))
+          .AddOverlayable(name_three, {})
+          .Build();
+
+  ResourceTable output_table;
+  ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &output_table));
+
+  auto search_result = output_table.FindResource(test::ParseNameOrDie(name_zero));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kProduct);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProductServices);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 2);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kPublic);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kSystem);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 3);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kSystem);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[1].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[1].policy,
+            Overlayable::Policy::kProduct);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[2].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[2].policy,
+            Overlayable::Policy::kProductServices);
+
+  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations.size(), 1);
+  EXPECT_TRUE(search_result.value().entry->overlayable_declarations[0].policy);
+  EXPECT_EQ(search_result.value().entry->overlayable_declarations[0].policy,
+            Overlayable::Policy::kPublic);
+
+}
+
+
 }  // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index d1a70a7..31d205e 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -298,19 +298,20 @@
                          "<colgroup align=\"left\" />\n"
                          "<tr><th>Attribute</th><th>Description</th></tr>\n";
 
-    // Build the table of attributes with their links and names.
-    for (const StyleableAttr& entry : sorted_attributes) {
-      if (SkipSymbol(entry.symbol)) {
-        continue;
-      }
-
+    // Removed and hidden attributes are public but hidden from the documentation, so don't emit
+    // them as part of the class documentation.
+    std::vector<StyleableAttr> documentation_attrs = sorted_attributes;
+    auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(),
+                                                    documentation_attrs.end(),
+                                                    [&](StyleableAttr entry) -> bool {
       StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
-      if (attr_comment_line.contains("@removed")) {
-        // Removed attributes are public but hidden from the documentation, so
-        // don't emit them as part of the class documentation.
-        continue;
-      }
+      return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed")
+                                      || attr_comment_line.contains("@hide");
+    });
+    documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end());
 
+    // Build the table of attributes with their links and names.
+    for (const StyleableAttr& entry : documentation_attrs) {
       const ResourceName& attr_name = entry.attr_ref->name.value();
       styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
                         << (!attr_name.package.empty() ? attr_name.package
@@ -320,16 +321,14 @@
       // Only use the comment up until the first '.'. This is to stay compatible with
       // the way old AAPT did it (presumably to keep it short and to avoid including
       // annotations like @hide which would affect this Styleable).
+      StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
       styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
                         << "</td></tr>\n";
     }
     styleable_comment << "</table>\n";
 
     // Generate the @see lines for each attribute.
-    for (const StyleableAttr& entry : sorted_attributes) {
-      if (SkipSymbol(entry.symbol)) {
-        continue;
-      }
+    for (const StyleableAttr& entry : documentation_attrs) {
       styleable_comment << "@see #" << entry.field_name << "\n";
     }
 
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index fa208be..4f51fc4 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -366,6 +366,46 @@
   ASSERT_TRUE(generator.Generate("android", &out));
   out.Flush();
 
+  EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
+  EXPECT_THAT(output, HasSubstr("@see #Container_one"));
+  EXPECT_THAT(output, HasSubstr("attr name android:one"));
+  EXPECT_THAT(output, HasSubstr("attr description"));
+  EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+  EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
+  Attribute attr;
+  attr.SetComment(StringPiece("This is an attribute @hide"));
+
+  Styleable styleable;
+  styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
+  styleable.SetComment(StringPiece("This is a styleable"));
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("android", 0x01)
+          .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+          .AddValue("android:styleable/Container",
+                    std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"android"})
+          .Build();
+  JavaClassGeneratorOptions options;
+  options.use_final = false;
+  JavaClassGenerator generator(context.get(), table.get(), options);
+
+  std::string output;
+  StringOutputStream out(&output);
+  ASSERT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
+  EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
   EXPECT_THAT(output, HasSubstr("attr name android:one"));
   EXPECT_THAT(output, HasSubstr("attr description"));
   EXPECT_THAT(output, HasSubstr(attr.GetComment()));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 3a5d585..1b6626a 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -368,7 +368,16 @@
         // Symbol state information may be lost if there is no value for the resource.
         if (entry->visibility.level != Visibility::Level::kUndefined && entry->values.empty()) {
           context->GetDiagnostics()->Error(DiagMessage(entry->visibility.source)
-                                           << "no definition for declared symbol '" << name << "'");
+                                               << "no definition for declared symbol '" << name
+                                               << "'");
+          error = true;
+        }
+
+        // Ensure that definitions for values declared as overlayable exist
+        if (!entry->overlayable_declarations.empty() && entry->values.empty()) {
+          context->GetDiagnostics()->Error(DiagMessage(entry->overlayable_declarations[0].source)
+                                           << "no definition for overlayable symbol '"
+                                           << name << "'");
           error = true;
         }
 
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index e50c70d..f25fcdc 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -19,6 +19,6 @@
         echo "If your change contains no confidential details (such as security fixes), please"
         echo "upload and merge this change at https://android-review.googlesource.com/."
         echo
-        exit 77
+        exit 1
     fi
 fi
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 91cd1cb..cb8fef9 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1285,10 +1285,19 @@
     if clazz.fullname == "android.os.UserManager": return
 
     for m in clazz.methods:
-        if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue
         if re.match("on[A-Z]+", m.name): continue
-        if "android.os.UserHandle" in m.args:
-            warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'")
+
+        has_arg = "android.os.UserHandle" in m.args
+        has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser")
+
+        if clazz.fullname.endswith("Manager") and has_arg:
+            warn(clazz, m, None, "When a method overload is needed to target a specific "
+                 "UserHandle, callers should be directed to use "
+                 "Context.createPackageContextAsUser() and re-obtain the relevant "
+                 "Manager, and no new API should be added")
+        elif has_arg and not has_name:
+            warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' "
+                 "or 'queryFooForUser'")
 
 
 def verify_params(clazz):
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
new file mode 100755
index 0000000..2291e5a
--- /dev/null
+++ b/tools/hiddenapi/exclude.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+set -e
+# Make sure that entries are not added for packages that are already fully handled using
+# annotations.
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
+# Each team should add a <team>_PACKAGES and <team>_EMAIL with the list of packages and
+# the team email to use in the event of this detecting an entry in a <team> package. Also
+# add <team> to the TEAMS list. 
+LIBCORE_PACKAGES="\
+  android.icu \
+  android.system \
+  com.android.bouncycastle \
+  com.android.conscrypt \
+  com.android.okhttp \
+  com.sun \
+  dalvik \
+  java \
+  javax \
+  libcore \
+  org.apache.harmony \
+  org.json \
+  org.w3c.dom \
+  org.xml.sax \
+  sun \
+  "
+LIBCORE_EMAIL=libcore-team@android.com
+
+# List of teams.
+TEAMS=LIBCORE
+
+# Generate the list of packages and convert to a regular expression.
+PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
+RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+    ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file))
+    if [[ -n "${ENTRIES}" ]]; then
+      echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+      echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
+      echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
+      # Partition the entries by team and provide contact details to aid in fixing the issue.
+      for t in ${TEAMS}
+      do
+        PACKAGES=$(eval echo \${${t}_PACKAGES})
+        RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+        TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+        if [[ -n "${TEAM_ENTRIES}" ]]; then
+          EMAIL=$(eval echo \${${t}_EMAIL})
+          echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
+          for i in ${ENTRIES}
+          do
+            echo -e "\e[33m  ${i}\e[0m"
+          done
+        fi
+      done
+      exit 1
+    fi
+done
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index fdc800b..01728fa1 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -15,23 +15,56 @@
 # limitations under the License.
 """
 Generate API lists for non-SDK API enforcement.
-
-usage: generate-hiddenapi-lists.py [-h]
-                                   --input-public INPUT_PUBLIC
-                                   --input-private INPUT_PRIVATE
-                                   [--input-whitelists [INPUT_WHITELISTS [INPUT_WHITELISTS ...]]]
-                                   [--input-greylists [INPUT_GREYLISTS [INPUT_GREYLISTS ...]]]
-                                   [--input-blacklists [INPUT_BLACKLISTS [INPUT_BLACKLISTS ...]]]
-                                   --output-whitelist OUTPUT_WHITELIST
-                                   --output-light-greylist OUTPUT_LIGHT_GREYLIST
-                                   --output-dark-greylist OUTPUT_DARK_GREYLIST
-                                   --output-blacklist OUTPUT_BLACKLIST
 """
 import argparse
 import os
 import sys
 import re
 
+# Names of flags recognized by the `hiddenapi` tool.
+FLAG_WHITELIST = "whitelist"
+FLAG_GREYLIST = "greylist"
+FLAG_BLACKLIST = "blacklist"
+FLAG_GREYLIST_MAX_O = "greylist-max-o"
+FLAG_GREYLIST_MAX_P = "greylist-max-p"
+
+# List of all known flags.
+FLAGS = [
+    FLAG_WHITELIST,
+    FLAG_GREYLIST,
+    FLAG_BLACKLIST,
+    FLAG_GREYLIST_MAX_O,
+    FLAG_GREYLIST_MAX_P,
+]
+FLAGS_SET = set(FLAGS)
+
+# Suffix used in command line args to express that only known and
+# otherwise unassigned entries should be assign the given flag.
+# For example, the P dark greylist is checked in as it was in P,
+# but signatures have changes since then. The flag instructs this
+# script to skip any entries which do not exist any more.
+FLAG_IGNORE_CONFLICTS_SUFFIX = "-ignore-conflicts"
+
+# Regex patterns of fields/methods used in serialization. These are
+# considered public API despite being hidden.
+SERIALIZATION_PATTERNS = [
+    r'readObject\(Ljava/io/ObjectInputStream;\)V',
+    r'readObjectNoData\(\)V',
+    r'readResolve\(\)Ljava/lang/Object;',
+    r'serialVersionUID:J',
+    r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
+    r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
+    r'writeReplace\(\)Ljava/lang/Object;',
+]
+
+# Single regex used to match serialization API. It combines all the
+# SERIALIZATION_PATTERNS into a single regular expression.
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+
+# Predicates to be used with filter_apis.
+IS_UNASSIGNED = lambda api, flags: not flags
+IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
+
 def get_args():
     """Parses command line arguments.
 
@@ -39,21 +72,21 @@
         Namespace: dictionary of parsed arguments
     """
     parser = argparse.ArgumentParser()
-    parser.add_argument('--input-public', required=True, help='List of all public members')
-    parser.add_argument('--input-private', required=True, help='List of all private members')
-    parser.add_argument(
-        '--input-whitelists', nargs='*',
-        help='Lists of members to force on whitelist')
-    parser.add_argument(
-        '--input-greylists', nargs='*',
-        help='Lists of members to force on light greylist')
-    parser.add_argument(
-        '--input-blacklists', nargs='*',
-        help='Lists of members to force on blacklist')
-    parser.add_argument('--output-whitelist', required=True)
-    parser.add_argument('--output-light-greylist', required=True)
-    parser.add_argument('--output-dark-greylist', required=True)
-    parser.add_argument('--output-blacklist', required=True)
+    parser.add_argument('--output', required=True)
+    parser.add_argument('--public', required=True, help='list of all public entries')
+    parser.add_argument('--private', required=True, help='list of all private entries')
+    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+        help='CSV files to be merged into output')
+
+    for flag in FLAGS:
+        ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
+        parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
+            help='lists of entries with flag "' + flag + '"')
+        parser.add_argument('--' + ignore_conflicts_flag, dest=ignore_conflicts_flag, nargs='*',
+            default=[], metavar='TXT_FILE',
+            help='lists of entries with flag "' + flag +
+                 '". skip entry if missing or flag conflict.')
+
     return parser.parse_args()
 
 def read_lines(filename):
@@ -65,10 +98,13 @@
         filename (string): Path to the file to read from.
 
     Returns:
-        list: Lines of the loaded file as a list of strings.
+        Lines of the file as a list of string.
     """
     with open(filename, 'r') as f:
-        return filter(lambda line: not line.startswith('#'), f.readlines())
+        lines = f.readlines();
+    lines = filter(lambda line: not line.startswith('#'), lines)
+    lines = map(lambda line: line.strip(), lines)
+    return set(lines)
 
 def write_lines(filename, lines):
     """Writes list of lines into a file, overwriting the file it it exists.
@@ -77,167 +113,168 @@
         filename (string): Path to the file to be writting into.
         lines (list): List of strings to write into the file.
     """
+    lines = map(lambda line: line + '\n', lines)
     with open(filename, 'w') as f:
         f.writelines(lines)
 
-def move_between_sets(subset, src, dst, source = "<unknown>"):
-    """Removes a subset of elements from one set and add it to another.
+class FlagsDict:
+    def __init__(self, public_api, private_api):
+        # Bootstrap the entries dictionary.
 
-    Args:
-        subset (set): The subset of `src` to be moved from `src` to `dst`.
-        src (set): Source set. Must be a superset of `subset`.
-        dst (set): Destination set. Must be disjoint with `subset`.
-    """
-    assert src.issuperset(subset), (
-        "Error processing: {}\n"
-        "The following entries were not found:\n"
-        "{}"
-        "Please visit go/hiddenapi for more information.").format(
-            source, "".join(map(lambda x: "  " + str(x), subset.difference(src))))
-    assert dst.isdisjoint(subset)
-    # Order matters if `src` and `subset` are the same object.
-    dst.update(subset)
-    src.difference_update(subset)
+        # Check that the two sets do not overlap.
+        public_api_set = set(public_api)
+        private_api_set = set(private_api)
+        assert public_api_set.isdisjoint(private_api_set), (
+            "Lists of public and private API overlap. " +
+            "This suggests an issue with the `hiddenapi` build tool.")
 
-def get_package_name(signature):
-    """Returns the package name prefix of a class member signature.
+        # Compute the whole key set
+        self._dict_keyset = public_api_set.union(private_api_set)
 
-    Example: "Ljava/lang/String;->hashCode()J" --> "Ljava/lang/"
+        # Create a dict that creates entries for both public and private API,
+        # and assigns public API to the whitelist.
+        self._dict = {}
+        for api in public_api:
+            self._dict[api] = set([ FLAG_WHITELIST ])
+        for api in private_api:
+            self._dict[api] = set()
 
-    Args:
-        signature (string): Member signature
+    def _check_entries_set(self, keys_subset, source):
+        assert isinstance(keys_subset, set)
+        assert keys_subset.issubset(self._dict_keyset), (
+            "Error processing: {}\n"
+            "The following entries were unexpected:\n"
+            "{}"
+            "Please visit go/hiddenapi for more information.").format(
+                source, "".join(map(lambda x: "  " + str(x), keys_subset - self._dict_keyset)))
 
-    Returns
-        string: Package name of the given member
-    """
-    class_name_end = signature.find("->")
-    assert class_name_end != -1, "Invalid signature: {}".format(signature)
-    package_name_end = signature.rfind("/", 0, class_name_end)
-    assert package_name_end != -1, "Invalid signature: {}".format(signature)
-    return signature[:package_name_end + 1]
+    def _check_flags_set(self, flags_subset, source):
+        assert isinstance(flags_subset, set)
+        assert flags_subset.issubset(FLAGS_SET), (
+            "Error processing: {}\n"
+            "The following flags were not recognized: \n"
+            "{}\n"
+            "Please visit go/hiddenapi for more information.").format(
+                source, "\n".join(flags_subset - FLAGS_SET))
 
-def all_package_names(*args):
-    """Returns a set of packages names in given lists of member signatures.
+    def filter_apis(self, filter_fn):
+        """Returns APIs which match a given predicate.
 
-    Example: args = [ set([ "Lpkg1/ClassA;->foo()V", "Lpkg2/ClassB;->bar()J" ]),
-                      set([ "Lpkg1/ClassC;->baz()Z" ]) ]
-             return value = set([ "Lpkg1/", "Lpkg2" ])
+        This is a helper function which allows to filter on both signatures (keys) and
+        flags (values). The built-in filter() invokes the lambda only with dict's keys.
 
-    Args:
-        *args (list): List of sets to iterate over and extract the package names
-                      of its elements (member signatures)
+        Args:
+            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
 
-    Returns:
-        set: All package names extracted from the given lists of signatures.
-    """
-    packages = set()
-    for arg in args:
-        packages = packages.union(map(get_package_name, arg))
-    return packages
+        Returns:
+            A set of APIs which match the predicate.
+        """
+        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
 
-def move_all(src, dst):
-    """Moves all elements of one set to another.
+    def get_valid_subset_of_unassigned_apis(self, api_subset):
+        """Sanitizes a key set input to only include keys which exist in the dictionary
+        and have not been assigned any flags.
 
-    Args:
-        src (set): Source set. Will become empty.
-        dst (set): Destination set. Will contain all elements of `src`.
-    """
-    move_between_sets(src, src, dst)
+        Args:
+            entries_subset (set/list): Key set to be sanitized.
 
-def move_from_files(filenames, src, dst):
-    """Loads member signatures from a list of files and moves them to a given set.
+        Returns:
+            Sanitized key set.
+        """
+        assert isinstance(api_subset, set)
+        return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
 
-    Opens files in `filenames`, reads all their lines and moves those from `src`
-    set to `dst` set.
+    def generate_csv(self):
+        """Constructs CSV entries from a dictionary.
 
-    Args:
-        filenames (list): List of paths to files to be loaded.
-        src (set): Set that loaded lines should be moved from.
-        dst (set): Set that loaded lines should be moved to.
-    """
-    if filenames:
-        for filename in filenames:
-            move_between_sets(set(read_lines(filename)), src, dst, filename)
+        Returns:
+            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+        """
+        return sorted(map(lambda api: ",".join([api] + sorted(self._dict[api])), self._dict))
 
-def move_serialization(src, dst):
-    """Moves all members matching serialization API signatures between given sets.
+    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+        """Parses CSV entries and merges them into a given dictionary.
 
-    Args:
-        src (set): Set that will be searched for serialization API and that API
-                   will be removed from it.
-        dst (set): Set that serialization API will be moved to.
-    """
-    serialization_patterns = [
-        r'readObject\(Ljava/io/ObjectInputStream;\)V',
-        r'readObjectNoData\(\)V',
-        r'readResolve\(\)Ljava/lang/Object;',
-        r'serialVersionUID:J',
-        r'serialPersistentFields:\[Ljava/io/ObjectStreamField;',
-        r'writeObject\(Ljava/io/ObjectOutputStream;\)V',
-        r'writeReplace\(\)Ljava/lang/Object;',
-    ]
-    regex = re.compile(r'.*->(' + '|'.join(serialization_patterns) + r')$')
-    move_between_sets(filter(lambda api: regex.match(api), src), src, dst)
+        The expected CSV format is:
+            <api signature>,<flag1>,<flag2>,...,<flagN>
 
-def move_from_packages(packages, src, dst):
-    """Moves all members of given package names from one set to another.
+        Args:
+            csv_lines (list of strings): Lines read from a CSV file.
+            source (string): Origin of `csv_lines`. Will be printed in error messages.
 
-    Args:
-        packages (list): List of string package names.
-        src (set): Set that will be searched for API matching one of the given
-                   package names. Surch API will be removed from the set.
-        dst (set): Set that matching API will be moved to.
-    """
-    move_between_sets(filter(lambda api: get_package_name(api) in packages, src), src, dst)
+        Throws:
+            AssertionError if parsed API signatures of flags are invalid.
+        """
+        # Split CSV lines into arrays of values.
+        csv_values = [ line.split(',') for line in csv_lines ]
+
+        # Check that all entries exist in the dict.
+        csv_keys = set([ csv[0] for csv in csv_values ])
+        self._check_entries_set(csv_keys, source)
+
+        # Check that all flags are known.
+        csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
+        self._check_flags_set(csv_flags, source)
+
+        # Iterate over all CSV lines, find entry in dict and append flags to it.
+        for csv in csv_values:
+            self._dict[csv[0]].update(csv[1:])
+
+    def assign_flag(self, flag, apis, source="<unknown>"):
+        """Assigns a flag to given subset of entries.
+
+        Args:
+            flag (string): One of FLAGS.
+            apis (set): Subset of APIs to recieve the flag.
+            source (string): Origin of `entries_subset`. Will be printed in error messages.
+
+        Throws:
+            AssertionError if parsed API signatures of flags are invalid.
+        """
+        # Check that all APIs exist in the dict.
+        self._check_entries_set(apis, source)
+
+        # Check that the flag is known.
+        self._check_flags_set(set([ flag ]), source)
+
+        # Iterate over the API subset, find each entry in dict and assign the flag to it.
+        for api in apis:
+            self._dict[api].add(flag)
 
 def main(argv):
-    args = get_args()
+    # Parse arguments.
+    args = vars(get_args())
 
-    # Initialize API sets by loading lists of public and private API. Public API
-    # are all members resolvable from SDK API stubs, other members are private.
-    # As an optimization, skip the step of moving public API from a full set of
-    # members and start with a populated whitelist.
-    whitelist = set(read_lines(args.input_public))
-    uncategorized = set(read_lines(args.input_private))
-    light_greylist = set()
-    dark_greylist = set()
-    blacklist = set()
+    flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
 
-    # Assert that there is no overlap between public and private API.
-    assert whitelist.isdisjoint(uncategorized)
-    num_all_api = len(whitelist) + len(uncategorized)
+    # Combine inputs which do not require any particular order.
+    # (1) Assign serialization API to whitelist.
+    flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
 
-    # Read all files which manually assign members to specific lists.
-    move_from_files(args.input_whitelists, uncategorized, whitelist)
-    move_from_files(args.input_greylists, uncategorized, light_greylist)
-    move_from_files(args.input_blacklists, uncategorized, blacklist)
+    # (2) Merge input CSV files into the dictionary.
+    for filename in args["csv"]:
+        flags.parse_and_merge_csv(read_lines(filename), filename)
 
-    # Iterate over all uncategorized members and move serialization API to whitelist.
-    move_serialization(uncategorized, whitelist)
+    # (3) Merge text files with a known flag into the dictionary.
+    for flag in FLAGS:
+        for filename in args[flag]:
+            flags.assign_flag(flag, read_lines(filename), filename)
 
-    # Extract package names of members from whitelist and light greylist, which
-    # are assumed to have been finalized at this point. Assign all uncategorized
-    # members from the same packages to the dark greylist.
-    dark_greylist_packages = all_package_names(whitelist, light_greylist)
-    move_from_packages(dark_greylist_packages, uncategorized, dark_greylist)
+    # Merge text files where conflicts should be ignored.
+    # This will only assign the given flag if:
+    # (a) the entry exists, and
+    # (b) it has not been assigned any other flag.
+    # Because of (b), this must run after all strict assignments have been performed.
+    for flag in FLAGS:
+        for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
+            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
+            flags.assign_flag(flag, valid_entries, filename)
 
-    # Assign all uncategorized members to the blacklist.
-    move_all(uncategorized, blacklist)
+    # Assign all remaining entries to the blacklist.
+    flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
 
-    # Assert we have not missed anything.
-    assert whitelist.isdisjoint(light_greylist)
-    assert whitelist.isdisjoint(dark_greylist)
-    assert whitelist.isdisjoint(blacklist)
-    assert light_greylist.isdisjoint(dark_greylist)
-    assert light_greylist.isdisjoint(blacklist)
-    assert dark_greylist.isdisjoint(blacklist)
-    assert num_all_api == len(whitelist) + len(light_greylist) + len(dark_greylist) + len(blacklist)
-
-    # Write final lists to disk.
-    write_lines(args.output_whitelist, whitelist)
-    write_lines(args.output_light_greylist, light_greylist)
-    write_lines(args.output_dark_greylist, dark_greylist)
-    write_lines(args.output_blacklist, blacklist)
+    # Write output.
+    write_lines(args["output"], flags.generate_csv())
 
 if __name__ == "__main__":
     main(sys.argv)
diff --git a/tools/hiddenapi/generate_hiddenapi_lists_test.py b/tools/hiddenapi/generate_hiddenapi_lists_test.py
index 4716241..249f37d 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists_test.py
@@ -2,14 +2,14 @@
 #
 # Copyright (C) 2018 The Android Open Source Project
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
+# 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,
+# 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.
@@ -18,90 +18,90 @@
 from generate_hiddenapi_lists import *
 
 class TestHiddenapiListGeneration(unittest.TestCase):
+    def test_init(self):
+        # Check empty lists
+        flags = FlagsDict([], [])
+        self.assertEquals(flags.generate_csv(), [])
 
-    def test_move_between_sets(self):
-        A = set([1, 2, 3, 4])
-        B = set([5, 6, 7, 8])
-        move_between_sets(set([2, 4]), A, B)
-        self.assertEqual(A, set([1, 3]))
-        self.assertEqual(B, set([2, 4, 5, 6, 7, 8]))
+        # Check valid input - two public and two private API signatures.
+        flags = FlagsDict(['A', 'B'], ['C', 'D'])
+        self.assertEquals(flags.generate_csv(),
+                          [ 'A,' + FLAG_WHITELIST, 'B,' + FLAG_WHITELIST, 'C', 'D' ])
 
-    def test_move_between_sets_fail_not_superset(self):
-        A = set([1, 2, 3, 4])
-        B = set([5, 6, 7, 8])
-        with self.assertRaises(AssertionError) as ar:
-            move_between_sets(set([0, 2]), A, B)
+        # Check invalid input - overlapping public/private API signatures.
+        with self.assertRaises(AssertionError):
+            flags = FlagsDict(['A', 'B'], ['B', 'C', 'D'])
 
-    def test_move_between_sets_fail_not_disjoint(self):
-        A = set([1, 2, 3, 4])
-        B = set([4, 5, 6, 7, 8])
-        with self.assertRaises(AssertionError) as ar:
-            move_between_sets(set([1, 4]), A, B)
+    def test_filter_apis(self):
+        # Initialize flags so that A and B are put on the whitelist and
+        # C, D, E are left unassigned. Try filtering for the unassigned ones.
+        flags = FlagsDict(['A', 'B'], ['C', 'D', 'E'])
+        filter_set = flags.filter_apis(lambda api, flags: not flags)
+        self.assertTrue(isinstance(filter_set, set))
+        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
 
-    def test_get_package_name(self):
-        self.assertEqual(get_package_name("Ljava/lang/String;->clone()V"), "Ljava/lang/")
+    def test_get_valid_subset_of_unassigned_keys(self):
+        # Create flags where only A is unassigned.
+        flags = FlagsDict(['A'], ['B', 'C'])
+        flags.assign_flag(FLAG_GREYLIST, set(['C']))
+        self.assertEquals(flags.generate_csv(),
+            [ 'A,' + FLAG_WHITELIST, 'B', 'C,' + FLAG_GREYLIST ])
 
-    def test_get_package_name_fail_no_arrow(self):
-        with self.assertRaises(AssertionError) as ar:
-            get_package_name("Ljava/lang/String;-clone()V")
-        with self.assertRaises(AssertionError) as ar:
-            get_package_name("Ljava/lang/String;>clone()V")
-        with self.assertRaises(AssertionError) as ar:
-            get_package_name("Ljava/lang/String;__clone()V")
-
-    def test_get_package_name_fail_no_package(self):
-        with self.assertRaises(AssertionError) as ar:
-            get_package_name("LString;->clone()V")
-
-    def test_all_package_names(self):
-        self.assertEqual(all_package_names(), set())
-        self.assertEqual(all_package_names(set(["Lfoo/Bar;->baz()V"])), set(["Lfoo/"]))
+        # Check three things:
+        # (1) B is selected as valid unassigned
+        # (2) A is not selected because it is assigned 'whitelist'
+        # (3) D is not selected because it is not a valid key
         self.assertEqual(
-            all_package_names(set(["Lfoo/Bar;->baz()V", "Lfoo/BarX;->bazx()I"])),
-            set(["Lfoo/"]))
-        self.assertEqual(
-            all_package_names(
-                set(["Lfoo/Bar;->baz()V"]),
-                set(["Lfoo/BarX;->bazx()I", "Labc/xyz/Mno;->ijk()J"])),
-            set(["Lfoo/", "Labc/xyz/"]))
+            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
 
-    def test_move_all(self):
-        src = set([ "abc", "xyz" ])
-        dst = set([ "def" ])
-        move_all(src, dst)
-        self.assertEqual(src, set())
-        self.assertEqual(dst, set([ "abc", "def", "xyz" ]))
+    def test_parse_and_merge_csv(self):
+        flags = FlagsDict(['A'], ['B'])
+        self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
 
-    def test_move_from_packages(self):
-        src = set([ "Lfoo/bar/ClassA;->abc()J",        # will be moved
-                    "Lfoo/bar/ClassA;->def()J",        # will be moved
-                    "Lcom/pkg/example/ClassD;->ijk:J", # not moved: different package
-                    "Lfoo/bar/xyz/ClassC;->xyz()Z" ])  # not moved: subpackage
-        dst = set()
-        packages = set([ "Lfoo/bar/" ])
-        move_from_packages(packages, src, dst)
-        self.assertEqual(
-            src, set([ "Lfoo/bar/xyz/ClassC;->xyz()Z", "Lcom/pkg/example/ClassD;->ijk:J" ]))
-        self.assertEqual(
-            dst, set([ "Lfoo/bar/ClassA;->abc()J", "Lfoo/bar/ClassA;->def()J" ]))
+        # Test empty CSV entry.
+        flags.parse_and_merge_csv(['B'])
+        self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
 
-    def test_move_serialization(self):
-        # All the entries should be moved apart from the last one
-        src = set([ "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)V",
-                    "Lfoo/bar/ClassA;->readObjectNoData()V",
-                    "Lfoo/bar/ClassA;->readResolve()Ljava/lang/Object;",
-                    "Lfoo/bar/ClassA;->serialVersionUID:J",
-                    "Lfoo/bar/ClassA;->serialPersistentFields:[Ljava/io/ObjectStreamField;",
-                    "Lfoo/bar/ClassA;->writeObject(Ljava/io/ObjectOutputStream;)V",
-                    "Lfoo/bar/ClassA;->writeReplace()Ljava/lang/Object;",
-                    # Should not be moved as signature does not match
-                    "Lfoo/bar/ClassA;->readObject(Ljava/io/ObjectInputStream;)I"])
-        expectedToMove = len(src) - 1
-        dst = set()
-        packages = set([ "Lfoo/bar/" ])
-        move_serialization(src, dst)
-        self.assertEqual(len(src), 1)
-        self.assertEqual(len(dst), expectedToMove)
+        # Test assigning an already assigned flag.
+        flags.parse_and_merge_csv(['A,' + FLAG_WHITELIST])
+        self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+        # Test new additions.
+        flags.parse_and_merge_csv([
+            'A,' + FLAG_GREYLIST,
+            'B,' + FLAG_BLACKLIST + ',' + FLAG_GREYLIST_MAX_O ])
+        self.assertEqual(flags.generate_csv(),
+            [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST,
+              'B,' + FLAG_BLACKLIST + "," + FLAG_GREYLIST_MAX_O ])
+
+        # Test unknown API signature.
+        with self.assertRaises(AssertionError):
+            flags.parse_and_merge_csv([ 'C' ])
+
+        # Test unknown flag.
+        with self.assertRaises(AssertionError):
+            flags.parse_and_merge_csv([ 'A,foo' ])
+
+    def test_assign_flag(self):
+        flags = FlagsDict(['A'], ['B'])
+        self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+        # Test assigning an already assigned flag.
+        flags.assign_flag(FLAG_WHITELIST, set([ 'A' ]))
+        self.assertEquals(flags.generate_csv(), [ 'A,' + FLAG_WHITELIST, 'B' ])
+
+        # Test new additions.
+        flags.assign_flag(FLAG_GREYLIST, set([ 'A', 'B' ]))
+        self.assertEquals(flags.generate_csv(),
+            [ 'A,' + FLAG_GREYLIST + "," + FLAG_WHITELIST, 'B,' + FLAG_GREYLIST ])
+
+        # Test invalid API signature.
+        with self.assertRaises(AssertionError):
+            flags.assign_flag(FLAG_WHITELIST, set([ 'C' ]))
+
+        # Test invalid flag.
+        with self.assertRaises(AssertionError):
+            flags.assign_flag('foo', set([ 'A' ]))
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
index 3f42275..faa3547 100644
--- a/tools/incident_section_gen/main.cpp
+++ b/tools/incident_section_gen/main.cpp
@@ -262,8 +262,9 @@
                 return false;
             case FieldDescriptor::TYPE_STRING:
                 if (getPrivacyFlags(field).patterns_size() != 0) return false;
+                break;
             default:
-                continue;
+                break;
         }
     }
     parents->erase(descriptor->full_name());
diff --git a/tools/powermodel/Android.bp b/tools/powermodel/Android.bp
new file mode 100644
index 0000000..f597aab
--- /dev/null
+++ b/tools/powermodel/Android.bp
@@ -0,0 +1,26 @@
+
+java_library_host {
+    name: "powermodel",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "guava",
+    ],
+}
+
+java_test_host {
+    name: "powermodel-test",
+
+    test_suites: ["general-tests"],
+
+    srcs: ["test/**/*.java"],
+    java_resource_dirs: ["test-resource"],
+
+    static_libs: [
+        "powermodel",
+        "junit",
+        "mockito",
+    ],
+}
+
diff --git a/tools/powermodel/TEST_MAPPING b/tools/powermodel/TEST_MAPPING
new file mode 100644
index 0000000..c8db339
--- /dev/null
+++ b/tools/powermodel/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "powermodel-test"
+    }
+  ]
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ActivityReport.java b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
new file mode 100644
index 0000000..4a8f633
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ActivityReport.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * ActivityReport contains the summary of the activity that consumes power
+ * as reported by batterystats or statsd.
+ */
+public class ActivityReport {
+    private AppList<AppActivity> mApps;
+
+    public ImmutableList<AppActivity> getAllApps() {
+        return mApps.getAllApps();
+    }
+
+    public ImmutableList<AppActivity> getRegularApps() {
+        return mApps.getRegularApps();
+    }
+
+    public List<AppActivity> findApp(String pkg) {
+        return mApps.findApp(pkg);
+    }
+
+    public AppActivity findApp(SpecialApp specialApp) {
+        return mApps.findApp(specialApp);
+    }
+
+    /**
+     * Find a component in the GLOBAL app.
+     * <p>
+     * Returns null if either the global app doesn't exist (bad data?) or the component
+     * doesn't exist in the global app.
+     */
+    public ComponentActivity findGlobalComponent(Component component) {
+         final AppActivity global = mApps.findApp(SpecialApp.GLOBAL);
+         if (global == null) {
+             return null;
+         }
+         return global.getComponentActivity(component);
+    }
+
+    public static class Builder {
+        private AppList.Builder<AppActivity,AppActivity.Builder> mApps = new AppList.Builder();
+
+        public Builder() {
+        }
+
+        public ActivityReport build() {
+            final ActivityReport result = new ActivityReport();
+            result.mApps = mApps.build();
+            return result;
+        }
+
+        public void addActivity(Component component, Collection<ComponentActivity> activities) {
+            for (final ComponentActivity activity: activities) {
+                addActivity(component, activity);
+            }
+        }
+
+        public void addActivity(Component component, ComponentActivity activity) {
+            AppActivity.Builder app = mApps.get(activity.attribution);
+            if (app == null) {
+                app = new AppActivity.Builder();
+                app.setAttribution(activity.attribution);
+                mApps.put(activity.attribution, app);
+            }
+            app.addComponentActivity(component, activity);
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppActivity.java b/tools/powermodel/src/com/android/powermodel/AppActivity.java
new file mode 100644
index 0000000..b87426c
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppActivity.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.HashMap;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class AppActivity extends AppInfo {
+
+    private ImmutableMap<Component, ComponentActivity> mComponents;
+    // TODO: power rails
+    // private ImmutableMap<Component, PowerRailActivity> mRails;
+
+    private AppActivity() {
+    }
+
+    /**
+     * Returns the {@link ComponentActivity} for the {@link Component} provided,
+     * or null if this AppActivity does not have that component.
+     * @more
+     * If there is no ComponentActivity for a particular Component, then
+     * there was no usage associated with that app for the app in question.
+     */
+    public ComponentActivity getComponentActivity(Component component) {
+        return mComponents.get(component);
+    }
+
+    public ImmutableSet<Component> getComponents() {
+        return mComponents.keySet();
+    }
+
+    public ImmutableMap<Component,ComponentActivity> getComponentActivities() {
+        return mComponents;
+    }
+
+    // TODO: power rails
+    // public ComponentActivity getPowerRail(Component component) {
+    //     return mComponents.get(component);
+    // }
+    //
+    // public Set<Component> getPowerRails() {
+    //     return mComponents.keySet();
+    // }
+
+    public static class Builder extends AppInfo.Builder<AppActivity> {
+        private HashMap<Component, ComponentActivity> mComponents = new HashMap();
+        // TODO power rails.
+        
+        public Builder() {
+        }
+
+        public AppActivity build() {
+            final AppActivity result = new AppActivity();
+            init(result);
+            result.mComponents = ImmutableMap.copyOf(mComponents);
+            return result;
+        }
+
+        public void addComponentActivity(Component component, ComponentActivity activity) {
+            mComponents.put(component, activity);
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppInfo.java b/tools/powermodel/src/com/android/powermodel/AppInfo.java
new file mode 100644
index 0000000..208339e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+class AppInfo {
+    private AttributionKey mAttribution;
+
+    protected AppInfo() {
+    }
+
+    public boolean hasPackage(String pkg) {
+        return mAttribution.hasPackage(pkg);
+    }
+
+    public AttributionKey getAttribution() {
+        return mAttribution;
+    }
+
+    abstract static class Builder<APP extends AppInfo> {
+        private AttributionKey mAttribution;
+
+        public Builder() {
+        }
+
+        public abstract APP build();
+
+        protected void init(AppInfo app) {
+            if (mAttribution == null) {
+                throw new RuntimeException("setAttribution(AttributionKey attribution) not called");
+            }
+            app.mAttribution = mAttribution;
+        }
+
+        public void setAttribution(AttributionKey attribution) {
+            mAttribution = attribution;
+        }
+
+        public AttributionKey getAttribution() {
+            return mAttribution;
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppList.java b/tools/powermodel/src/com/android/powermodel/AppList.java
new file mode 100644
index 0000000..19572fc
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppList.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+class AppList<APP extends AppInfo> {
+    private ImmutableList<APP> mAllApps;
+    private ImmutableList<APP> mRegularApps;
+    private ImmutableMap<SpecialApp,APP> mSpecialApps;
+
+    private AppList() {
+    }
+
+    public ImmutableList<APP> getAllApps() {
+        return mAllApps;
+    }
+
+    public ImmutableList<APP> getRegularApps() {
+        return mRegularApps;
+    }
+
+    public List<APP> findApp(String pkg) {
+        List<APP> result = new ArrayList();
+        for (APP app: mRegularApps) {
+            if (app.hasPackage(pkg)) {
+                result.add(app);
+            }
+        }
+        return result;
+    }
+
+    public APP findApp(SpecialApp specialApp) {
+        return mSpecialApps.get(specialApp);
+    }
+
+    public static class Builder<APP extends AppInfo, BUILDER extends AppInfo.Builder<APP>> {
+        private final HashMap<AttributionKey,BUILDER> mApps = new HashMap();
+
+        public Builder() {
+        }
+
+        public AppList<APP> build() {
+            final AppList<APP> result = new AppList();
+            final ArrayList<APP> allApps = new ArrayList();
+            final ArrayList<APP> regularApps = new ArrayList();
+            final HashMap<SpecialApp,APP> specialApps = new HashMap();
+            for (AppInfo.Builder<APP> app: mApps.values()) {
+                final AttributionKey attribution = app.getAttribution();
+                final APP appActivity = app.build();
+                allApps.add(appActivity);
+                if (attribution.isSpecialApp()) {
+                    specialApps.put(attribution.getSpecialApp(), appActivity);
+                } else {
+                    regularApps.add(appActivity);
+                }
+            }
+            result.mAllApps = ImmutableList.copyOf(allApps);
+            result.mRegularApps = ImmutableList.copyOf(regularApps);
+            result.mSpecialApps = ImmutableMap.copyOf(specialApps);
+            return result;
+        }
+
+        public BUILDER get(AttributionKey attribution) {
+            return mApps.get(attribution);
+        }
+
+        public BUILDER put(AttributionKey attribution, BUILDER app) {
+            return mApps.put(attribution, app);
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AppPower.java b/tools/powermodel/src/com/android/powermodel/AppPower.java
new file mode 100644
index 0000000..283982b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AppPower.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+public class AppPower extends AppInfo {
+    private ImmutableMap<Component, ComponentPower> mComponents;
+
+    private double mAppPowerMah;
+
+
+    private AppPower() {
+    }
+
+    /**
+     * Returns the {@link ComponentPower} for the {@link Component} provided,
+     * or null if this AppPower does not have that component.
+     * @more
+     * If the component was in the power profile for this device, there
+     * will be a component for it, even if there was no power used
+     * by that component. In that case, the
+     * {@link ComponentPower.getUsage() ComponentPower.getUsage()}
+     * method will return 0.
+     */
+    public ComponentPower getComponentPower(Component component) {
+        return mComponents.get(component);
+    }
+
+    public Set<Component> getComponents() {
+        return mComponents.keySet();
+    }
+
+    /**
+     * Return the total power used by this app.
+     */
+    public double getAppPowerMah() {
+        return mAppPowerMah;
+    }
+
+    /**
+     * Builder class for {@link AppPower}
+     */
+    public static class Builder extends AppInfo.Builder<AppPower> {
+        private HashMap<Component, ComponentPower> mComponents = new HashMap();
+
+        public Builder() {
+        }
+
+        public AppPower build() {
+            final AppPower result = new AppPower();
+            init(result);
+            result.mComponents = ImmutableMap.copyOf(mComponents);
+
+            // Add up the components
+            double appPowerMah = 0;
+            for (final ComponentPower componentPower: mComponents.values()) {
+                appPowerMah += componentPower.powerMah;
+            }
+            result.mAppPowerMah = appPowerMah;
+
+            return result;
+        }
+
+        public void addComponentPower(Component component, ComponentPower componentPower) {
+            mComponents.put(component, componentPower);
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/AttributionKey.java b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
new file mode 100644
index 0000000..f19e0b7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/AttributionKey.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.common.collect.ImmutableSet;
+
+public class AttributionKey {
+    private final int mUid;
+    private final ImmutableSet<String> mPackages;
+    private final SpecialApp mSpecialApp;
+
+    public AttributionKey(SpecialApp specialApp) {
+        mUid = -1;
+        mPackages = ImmutableSet.of();
+        mSpecialApp = specialApp;
+    }
+
+    public AttributionKey(int uid, Set<String> packages) {
+        mUid = uid;
+        mPackages = ImmutableSet.copyOf(packages);
+        mSpecialApp = null;
+    }
+
+    public ImmutableSet<String> getPackages() {
+        return mPackages;
+    }
+
+    public boolean hasPackage(String pkg) {
+        return mPackages.contains(pkg);
+    }
+
+    public SpecialApp getSpecialApp() {
+        return mSpecialApp;
+    }
+
+    public boolean isSpecialApp() {
+        return mSpecialApp != null;
+    }
+
+    /**
+     * Returns the uid for this attribution, or -1 if there isn't one
+     * (e.g. if it is a special app).
+     */
+    public int getUid() {
+        return mUid;
+    }
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = (31 * hash) + (mUid);
+        hash = (31 * hash) + (mPackages == null ? 0 : mPackages.hashCode());
+        hash = (31 * hash) + (mSpecialApp == null ? 0 : mSpecialApp.hashCode());
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null) {
+            return false;
+        }
+        if (this.getClass() != o.getClass()) {
+            return false;
+        }
+        final AttributionKey that = (AttributionKey)o;
+        return (this.mUid == that.mUid)
+                && this.mPackages != null && this.mPackages.equals(that.mPackages)
+                && this.mSpecialApp != null && this.mSpecialApp.equals(that.mSpecialApp);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder str = new StringBuilder("AttributionKey(");
+        if (mUid >= 0) {
+            str.append(" uid=");
+            str.append(mUid);
+        }
+        if (mPackages.size() > 0) {
+            str.append(" packages=[");
+            for (String pkg: mPackages) {
+                str.append(' ');
+                str.append(pkg);
+            }
+            str.append(" ]");
+        }
+        if (mSpecialApp != null) {
+            str.append(" specialApp=");
+            str.append(mSpecialApp.name());
+        }
+        str.append(" )");
+        return str.toString();
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
new file mode 100644
index 0000000..595c661
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/BatteryStatsReader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import com.android.powermodel.component.ModemBatteryStatsReader;
+
+public class BatteryStatsReader {
+    /**
+     * Construct a reader.
+     */
+    public BatteryStatsReader() {
+    }
+
+    /**
+     * Parse a powermodel.xml file and return a PowerProfile object.
+     *
+     * @param stream An InputStream containing the batterystats output.
+     *
+     * @throws ParseException Thrown when the xml file can not be parsed.
+     * @throws IOException When there is a problem reading the stream.
+     */
+    public static ActivityReport parse(InputStream stream) throws ParseException, IOException {
+        final Parser parser = new Parser(stream);
+        return parser.parse();
+    }
+
+    /**
+     * Implements the reading and power model logic.
+     */
+    private static class Parser {
+        final InputStream mStream;
+        final ActivityReport mResult;
+        RawBatteryStats mBs;
+
+        /**
+         * Constructor to capture the parameters to read.
+         */
+        Parser(InputStream stream) {
+            mStream = stream;
+            mResult = new ActivityReport();
+        }
+
+        /**
+         * Read the stream, parse it, and apply the power model.
+         * Do not call this more than once.
+         */
+        ActivityReport parse() throws ParseException, IOException {
+            mBs = RawBatteryStats.parse(mStream);
+
+            final ActivityReport.Builder report = new ActivityReport.Builder();
+
+            report.addActivity(Component.MODEM, ModemBatteryStatsReader.createActivities(mBs));
+
+            return report.build();
+        }
+    }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/Component.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/Component.java
index 27d25b8..baae6d7 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/Component.java
@@ -13,12 +13,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
+
+package com.android.powermodel;
 
 /**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
+ * The hardware components that use power on a device.
  */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+public enum Component {
+    CPU,
+    SCREEN,
+    MODEM,
+    WIFI,
+    BLUETOOTH,
+    VIDEO,
+    AUDIO,
+    FLASHLIGHT,
+    CAMERA,
+    GPS,
 }
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentActivity.java b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
new file mode 100644
index 0000000..c1e2662
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+
+/**
+ * Encapsulates the work done by an app (including synthetic apps) that costs power.
+ */
+public class ComponentActivity {
+    public AttributionKey attribution;
+
+    protected ComponentActivity(AttributionKey attribution) {
+        this.attribution = attribution;
+    }
+
+    // TODO: Can we refactor what goes into the activities so this function
+    // doesn't need the global state?
+    /**
+     * Apply the power profile for this component.  Subclasses should implement this
+     * to do the per-component calculatinos.  The default implementation returns null.
+     * If this method returns null, then there will be no power associated for this
+     * component, which, for example is true with some of the GLOBAL activities.
+     */
+    public ComponentPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+        return null;
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/ComponentPower.java b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
new file mode 100644
index 0000000..b22ff87
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ComponentPower.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+/**
+ * The hardware component that uses power on a device.
+ * <p>
+ * This base class contains the total power used by each Component in an app.
+ * Subclasses may add more detail, which is a drill-down, but is not to be
+ * <i>added</i> to {@link #powerMah}.
+ */
+public abstract class ComponentPower<ACTIVITY extends ComponentActivity> {
+    /**
+     * The app associated with this ComponentPower.
+     */
+    public AttributionKey attribution;
+
+    /**
+     * The app activity that resulted in the power usage for this component.
+     */
+    public ACTIVITY activity;
+
+    /**
+     * The total power used by this component in this app.
+     */
+    public double powerMah;
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/ComponentProfile.java
index 27d25b8..e76e5fb 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/ComponentProfile.java
@@ -13,12 +13,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel;
+
+public class ComponentProfile {
 }
diff --git a/tools/powermodel/src/com/android/powermodel/CsvParser.java b/tools/powermodel/src/com/android/powermodel/CsvParser.java
new file mode 100644
index 0000000..78cd261
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/CsvParser.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+
+/**
+ * Parses CSV.
+ * <p>
+ * Call parse() with an InputStream.
+ * <p>
+ * CsvLineProcessor.onLine() will be called for each line in the source document.
+ * <p>
+ * To simplify parsing and to protect against using too much memory for bad
+ * data, the maximum field length is {@link #MAX_FIELD_SIZE}.
+ */
+class CsvParser {
+    /**
+     * The maximum size of a single field in bytes.
+     */
+    public static final int MAX_FIELD_SIZE = (8*1024)-1;
+
+    /**
+     * Callback interface for each line of CSV as it is parsed.
+     */
+    interface LineProcessor {
+        /**
+         * A line of CSV was parsed.
+         * 
+         * @param lineNumber the line number in the file, starting at 1
+         * @param fields the comma separated fields for the line
+         */
+        void onLine(int lineNumber, ArrayList<String> fields) throws ParseException;
+    }
+
+    /**
+     * Parse the CSV text in input, calling onto processor for each row.
+     */
+    public static void parse(InputStream input, LineProcessor processor)
+            throws IOException, ParseException {
+        final Charset utf8 = StandardCharsets.UTF_8;
+        final byte[] buf = new byte[MAX_FIELD_SIZE+1];
+        int lineNumber = 1;
+        int readPos = 0;
+        int prev = 0;
+        ArrayList<String> fields = new ArrayList<String>();
+        boolean finalBuffer = false;
+        boolean escaping = false;
+        boolean sawQuote = false;
+
+        while (!finalBuffer) {
+            int amt = input.read(buf, readPos, buf.length-readPos);
+            if (amt < 0) {
+                // No more data. Process whatever's left from before.
+                amt = readPos;
+                finalBuffer = true;
+            } else {
+                // Process whatever's left from before, plus the new data.
+                amt += readPos;
+                finalBuffer = false;
+            }
+
+            // Process as much of this buffer as we can.
+            int fieldStart = 0;
+            int index = readPos;
+            int escapeIndex = escaping ? readPos : -1;
+            while (index < amt) {
+                byte c = buf[index];
+                if (c == '\r' || c == '\n') {
+                    if (escaping) {
+                        // TODO: Quotes do not escape newlines in our CSV dialect,
+                        // but we actually see some data where it should.
+                        fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+                        escapeIndex = -1;
+                        escaping = false;
+                        sawQuote = false;
+                    } else {
+                        fields.add(new String(buf, fieldStart, index-fieldStart));
+                    }
+                    // Don't report blank lines
+                    if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+                        processor.onLine(lineNumber, fields);
+                    }
+                    fields = new ArrayList<String>();
+                    if (!(c == '\n' && prev == '\r')) {
+                        // Don't double increment for dos line endings.
+                        lineNumber++;
+                    }
+                    fieldStart = index = index + 1;
+                } else {
+                    if (escaping) {
+                        // Field started with a " so quotes are escaped with " and commas
+                        // don't matter except when following a single quote.
+                        if (c == '"') {
+                            if (sawQuote) {
+                                buf[escapeIndex] = buf[index];
+                                escapeIndex++;
+                                sawQuote = false;
+                            } else {
+                                sawQuote = true;
+                            }
+                            index++;
+                        } else if (sawQuote && c == ',') {
+                            fields.add(new String(buf, fieldStart, escapeIndex-fieldStart));
+                            fieldStart = index = index + 1;
+                            escapeIndex = -1;
+                            escaping = false;
+                            sawQuote = false;
+                        } else {
+                            buf[escapeIndex] = buf[index];
+                            escapeIndex++;
+                            index++;
+                            sawQuote = false;
+                        }
+                    } else {
+                        if (c == ',') {
+                            fields.add(new String(buf, fieldStart, index-fieldStart));
+                            fieldStart = index + 1;
+                        } else if (c == '"' && fieldStart == index) {
+                            // First character is a "
+                            escaping = true;
+                            fieldStart = escapeIndex = index + 1;
+                        }
+                        index++;
+                    }
+                }
+                prev = c;
+            }
+
+            // A single field is greater than buf.length, so fail.
+            if (fieldStart == 0 && index == buf.length) {
+                throw new ParseException(lineNumber, "Line is too long: "
+                        + new String(buf, 0, 20, utf8) + "...");
+            }
+
+            // Move whatever we didn't process to the beginning of the buffer
+            // and try again.
+            if (fieldStart != amt) {
+                readPos = (escaping ? escapeIndex : index) - fieldStart;
+                System.arraycopy(buf, fieldStart, buf, 0, readPos);
+            } else {
+                readPos = 0;
+            }
+        
+            // Process whatever's left over
+            if (finalBuffer) {
+                fields.add(new String(buf, 0, readPos));
+                // If there is any content, return the last line.
+                if (fields.size() > 1 || (fields.size() == 1 && fields.get(0).length() > 0)) {
+                    processor.onLine(lineNumber, fields);
+                }
+            }
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/ParseException.java b/tools/powermodel/src/com/android/powermodel/ParseException.java
new file mode 100644
index 0000000..e1f232b
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/ParseException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+public class ParseException extends Exception {
+    public final int line;
+
+    public ParseException(int line, String message, Throwable th) {
+        super(message, th);
+        this.line = line;
+    }
+
+    public ParseException(int line, String message) {
+        this(line, message, null);
+    }
+
+    public ParseException(String message) {
+        this(0, message, null);
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/PowerProfile.java b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
new file mode 100644
index 0000000..373a9c9
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerProfile.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import com.android.powermodel.util.Conversion;
+
+public class PowerProfile {
+
+    // Remaining fields from the android code for which the actual usage is unclear.
+    //   battery.capacity
+    //   bluetooth.controller.voltage
+    //   modem.controller.voltage
+    //   gps.voltage
+    //   wifi.controller.voltage
+    //   radio.on
+    //   radio.scanning
+    //   radio.active
+    //   memory.bandwidths
+    //   wifi.batchedscan
+    //   wifi.scan
+    //   wifi.on
+    //   wifi.active
+    //   wifi.controller.tx_levels
+
+    private static Pattern RE_CLUSTER_POWER = Pattern.compile("cpu.cluster_power.cluster([0-9]*)");
+    private static Pattern RE_CORE_SPEEDS = Pattern.compile("cpu.core_speeds.cluster([0-9]*)");
+    private static Pattern RE_CORE_POWER = Pattern.compile("cpu.core_power.cluster([0-9]*)");
+
+    private HashMap<Component, ComponentProfile> mComponents = new HashMap();
+
+    /**
+     * Which element we are currently parsing.
+     */
+    enum ElementState {
+        BEGIN,
+        TOP,
+        ITEM,
+        ARRAY,
+        VALUE
+    }
+
+    /**
+     * Implements the reading and power model logic.
+     */
+    private static class Parser {
+        private final InputStream mStream;
+        private final PowerProfile mResult;
+
+        // Builders for the ComponentProfiles.
+        private final AudioProfile mAudio = new AudioProfile();
+        private final BluetoothProfile mBluetooth = new BluetoothProfile();
+        private final CameraProfile mCamera = new CameraProfile();
+        private final CpuProfile.Builder mCpuBuilder = new CpuProfile.Builder();
+        private final FlashlightProfile mFlashlight = new FlashlightProfile();
+        private final GpsProfile.Builder mGpsBuilder = new GpsProfile.Builder();
+        private final ModemProfile.Builder mModemBuilder = new ModemProfile.Builder();
+        private final ScreenProfile mScreen = new ScreenProfile();
+        private final VideoProfile mVideo = new VideoProfile();
+        private final WifiProfile mWifi = new WifiProfile();
+
+        /**
+         * Constructor to capture the parameters to read.
+         */
+        Parser(InputStream stream) {
+            mStream = stream;
+            mResult = new PowerProfile();
+        }
+
+        /**
+         * Read the stream, parse it, and apply the power model.
+         * Do not call this more than once.
+         */
+        PowerProfile parse() throws ParseException {
+            final SAXParserFactory factory = SAXParserFactory.newInstance();
+            AndroidResourceHandler handler = null;
+            try {
+                final SAXParser saxParser = factory.newSAXParser();
+
+                handler = new AndroidResourceHandler() {
+                    @Override
+                    public void onItem(Locator locator, String name, float value)
+                            throws SAXParseException {
+                        Parser.this.onItem(locator, name, value);
+                    }
+
+                    @Override
+                    public void onArray(Locator locator, String name, float[] value)
+                            throws SAXParseException {
+                        Parser.this.onArray(locator, name, value);
+                    }
+                };
+
+                saxParser.parse(mStream, handler);
+            } catch (ParserConfigurationException ex) {
+                // Coding error, not runtime error.
+                throw new RuntimeException(ex);
+            } catch (SAXParseException ex) {
+                throw new ParseException(ex.getLineNumber(), ex.getMessage(), ex);
+            } catch (SAXException | IOException ex) {
+                // Make a guess about the line number.
+                throw new ParseException(handler.getLineNumber(), ex.getMessage(), ex);
+            }
+
+            // TODO: This doesn't cover the multiple algorithms. Some refactoring will
+            // be necessary.
+            mResult.mComponents.put(Component.AUDIO, mAudio);
+            mResult.mComponents.put(Component.BLUETOOTH, mBluetooth);
+            mResult.mComponents.put(Component.CAMERA, mCamera);
+            mResult.mComponents.put(Component.CPU, mCpuBuilder.build());
+            mResult.mComponents.put(Component.FLASHLIGHT, mFlashlight);
+            mResult.mComponents.put(Component.GPS, mGpsBuilder.build());
+            mResult.mComponents.put(Component.MODEM, mModemBuilder.build());
+            mResult.mComponents.put(Component.SCREEN, mScreen);
+            mResult.mComponents.put(Component.VIDEO, mVideo);
+            mResult.mComponents.put(Component.WIFI, mWifi);
+
+            return mResult;
+        }
+
+        /**
+         * Handles an item tag in the power_profile.xml.
+         */
+        public void onItem(Locator locator, String name, float value) throws SAXParseException {
+            Integer index;
+            try {
+                if ("ambient.on".equals(name)) {
+                    mScreen.ambientMa = value;
+                } else if ("audio".equals(name)) {
+                    mAudio.onMa = value;
+                } else if ("bluetooth.controller.idle".equals(name)) {
+                    mBluetooth.idleMa = value;
+                } else if ("bluetooth.controller.rx".equals(name)) {
+                    mBluetooth.rxMa = value;
+                } else if ("bluetooth.controller.tx".equals(name)) {
+                    mBluetooth.txMa = value;
+                } else if ("camera.avg".equals(name)) {
+                    mCamera.onMa = value;
+                } else if ("camera.flashlight".equals(name)) {
+                    mFlashlight.onMa = value;
+                } else if ("cpu.suspend".equals(name)) {
+                    mCpuBuilder.setSuspendMa(value);
+                } else if ("cpu.idle".equals(name)) {
+                    mCpuBuilder.setIdleMa(value);
+                } else if ("cpu.active".equals(name)) {
+                    mCpuBuilder.setActiveMa(value);
+                } else if ((index = matchIndexedRegex(locator, RE_CLUSTER_POWER, name)) != null) {
+                    mCpuBuilder.setClusterPower(index, value);
+                } else if ("gps.on".equals(name)) {
+                    mGpsBuilder.setOnMa(value);
+                } else if ("modem.controller.sleep".equals(name)) {
+                    mModemBuilder.setSleepMa(value);
+                } else if ("modem.controller.idle".equals(name)) {
+                    mModemBuilder.setIdleMa(value);
+                } else if ("modem.controller.rx".equals(name)) {
+                    mModemBuilder.setRxMa(value);
+                } else if ("radio.scanning".equals(name)) {
+                    mModemBuilder.setScanningMa(value);
+                } else if ("screen.on".equals(name)) {
+                    mScreen.onMa = value;
+                } else if ("screen.full".equals(name)) {
+                    mScreen.fullMa = value;
+                } else if ("video".equals(name)) {
+                    mVideo.onMa = value;
+                } else if ("wifi.controller.idle".equals(name)) {
+                    mWifi.idleMa = value;
+                } else if ("wifi.controller.rx".equals(name)) {
+                    mWifi.rxMa = value;
+                } else if ("wifi.controller.tx".equals(name)) {
+                    mWifi.txMa = value;
+                } else {
+                    // TODO: Uncomment this when we have all of the items parsed.
+                    // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+                    //        locator, ex);
+
+                }
+            } catch (ParseException ex) {
+                throw new SAXParseException(ex.getMessage(), locator, ex);
+            }
+        }
+
+        /**
+         * Handles an array tag in the power_profile.xml.
+         */
+        public void onArray(Locator locator, String name, float[] value) throws SAXParseException {
+            Integer index;
+            try {
+                if ("cpu.clusters.cores".equals(name)) {
+                    mCpuBuilder.setCoreCount(Conversion.toIntArray(value));
+                } else if ((index = matchIndexedRegex(locator, RE_CORE_SPEEDS, name)) != null) {
+                    mCpuBuilder.setCoreSpeeds(index, Conversion.toIntArray(value));
+                } else if ((index = matchIndexedRegex(locator, RE_CORE_POWER, name)) != null) {
+                    mCpuBuilder.setCorePower(index, value);
+                } else if ("gps.signalqualitybased".equals(name)) {
+                    mGpsBuilder.setSignalMa(value);
+                } else if ("modem.controller.tx".equals(name)) {
+                    mModemBuilder.setTxMa(value);
+                } else {
+                    // TODO: Uncomment this when we have all of the items parsed.
+                    // throw new SAXParseException("Unhandled <item name=\"" + name + "\"> element",
+                    //        locator, ex);
+                }
+            } catch (ParseException ex) {
+                throw new SAXParseException(ex.getMessage(), locator, ex);
+            }
+        }
+    }
+
+    /**
+     * SAX XML handler that can parse the android resource files.
+     * In our case, all elements are floats.
+     */
+    abstract static class AndroidResourceHandler extends DefaultHandler {
+        /**
+         * The set of names already processed. Map of name to line number.
+         */
+        private HashMap<String,Integer> mAlreadySeen = new HashMap<String,Integer>();
+
+        /**
+         * Where in the document we are parsing.
+         */
+        private Locator mLocator;
+
+        /**
+         * Which element we are currently parsing.
+         */
+        private ElementState mState = ElementState.BEGIN;
+
+        /**
+         * Saved name from item and array elements.
+         */
+        private String mName;
+
+        /**
+         * The text that is currently being captured, or null if {@link #startCapturingText()}
+         * has not been called.
+         */
+        private StringBuilder mText;
+
+        /**
+         * The array values that have been parsed so for for this array. Null if we are
+         * not inside an array tag.
+         */
+        private ArrayList<Float> mArray;
+
+        /**
+         * Called when an item tag is encountered.
+         */
+        public abstract void onItem(Locator locator, String name, float value)
+                throws SAXParseException;
+
+        /**
+         * Called when an array is encountered.
+         */
+        public abstract void onArray(Locator locator, String name, float[] value)
+                throws SAXParseException;
+
+        /**
+         * If we have a Locator set, return the line number, otherwise return 0.
+         */
+        public int getLineNumber() {
+            return mLocator != null ? mLocator.getLineNumber() : 0;
+        }
+
+        /**
+         * Handle setting the parse location object.
+         */
+        public void setDocumentLocator(Locator locator) {
+            mLocator = locator;
+        }
+
+        /**
+         * Handle beginning of an element.
+         *
+         * @param ns Namespace uri
+         * @param ln Local name (inside namespace)
+         * @param element Tag name
+         */
+        @Override
+        public void startElement(String ns, String ln, String element,
+                Attributes attr) throws SAXException {
+            switch (mState) {
+                case BEGIN:
+                    // Outer element, we don't care the tag name.
+                    mState = ElementState.TOP;
+                    return;
+                case TOP:
+                    if ("item".equals(element)) {
+                        mState = ElementState.ITEM;
+                        saveNameAttribute(attr);
+                        startCapturingText();
+                        return;
+                    } else if ("array".equals(element)) {
+                        mState = ElementState.ARRAY;
+                        mArray = new ArrayList<Float>();
+                        saveNameAttribute(attr);
+                        return;
+                    }
+                    break;
+                case ARRAY:
+                    if ("value".equals(element)) {
+                        mState = ElementState.VALUE;
+                        startCapturingText();
+                        return;
+                    }
+                    break;
+            }
+            throw new SAXParseException("unexpected element: '" + element + "'", mLocator);
+        }
+
+        /**
+         * Handle end of an element.
+         *
+         * @param ns Namespace uri
+         * @param ln Local name (inside namespace)
+         * @param element Tag name
+         */
+        @Override
+        public void endElement(String ns, String ln, String element) throws SAXException {
+            switch (mState) {
+                case ITEM: {
+                    float value = parseFloat(finishCapturingText());
+                    mState = ElementState.TOP;
+                    onItem(mLocator, mName, value);
+                    break;
+                }
+                case ARRAY: {
+                    final int N = mArray.size();
+                    float[] values = new float[N];
+                    for (int i=0; i<N; i++) {
+                        values[i] = mArray.get(i);
+                    }
+                    mArray = null;
+                    mState = ElementState.TOP;
+                    onArray(mLocator, mName, values);
+                    break;
+                }
+                case VALUE: {
+                    mArray.add(parseFloat(finishCapturingText()));
+                    mState = ElementState.ARRAY;
+                    break;
+                }
+            }
+        }
+
+        /**
+         * Interstitial text received.
+         *
+         * @throws SAXException if there shouldn't be non-whitespace text here
+         */
+        @Override
+        public void characters(char text[], int start, int length) throws SAXException {
+            if (mText == null && length > 0 && !isWhitespace(text, start, length)) {
+                throw new SAXParseException("unexpected text: '"
+                        + firstLine(text, start, length).trim() + "'", mLocator);
+            }
+            if (mText != null) {
+                mText.append(text, start, length);
+            }
+        }
+
+        /**
+         * Begin collecting text from inside an element.
+         */
+        private void startCapturingText() {
+            if (mText != null) {
+                throw new RuntimeException("ASSERTION FAILED: Shouldn't be already capturing"
+                        + " text. mState=" + mState.name()
+                        + " line=" + mLocator.getLineNumber()
+                        + " column=" + mLocator.getColumnNumber());
+            }
+            mText = new StringBuilder();
+        }
+
+        /**
+         * Stop capturing text from inside an element.
+         *
+         * @return the captured text
+         */
+        private String finishCapturingText() {
+            if (mText == null) {
+                throw new RuntimeException("ASSERTION FAILED: Should already be capturing"
+                        + " text. mState=" + mState.name()
+                        + " line=" + mLocator.getLineNumber()
+                        + " column=" + mLocator.getColumnNumber());
+            }
+            final String result = mText.toString().trim();
+            mText = null;
+            return result;
+        }
+
+        /**
+         * Get the "name" attribute.
+         *
+         * @throws SAXParseException if the name attribute is not present or if
+         * the name has already been seen in the file.
+         */
+        private void saveNameAttribute(Attributes attr) throws SAXParseException {
+            final String name = attr.getValue("name");
+            if (name == null) {
+                throw new SAXParseException("expected 'name' attribute", mLocator);
+            }
+            Integer prev = mAlreadySeen.put(name, mLocator.getLineNumber());
+            if (prev != null) {
+                throw new SAXParseException("name '" + name + "' already seen on line: " + prev,
+                        mLocator);
+            }
+            mName = name;
+        }
+
+        /**
+         * Gets the float value of the string.
+         *
+         * @throws SAXParseException if 'text' can't be parsed as a float.
+         */
+        private float parseFloat(String text) throws SAXParseException {
+            try {
+                return Float.parseFloat(text);
+            } catch (NumberFormatException ex) {
+                throw new SAXParseException("not a valid float value: '" + text + "'",
+                        mLocator, ex);
+            }
+        }
+    }
+
+    /**
+     * Return whether the given substring is all whitespace.
+     */
+    private static boolean isWhitespace(char[] text, int start, int length) {
+        for (int i = start; i < (start + length); i++) {
+            if (!Character.isSpace(text[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Return the contents of text up to the first newline.
+     */
+    private static String firstLine(char[] text, int start, int length) {
+        // TODO: The line number will be wrong if we skip preceeding blank lines.
+        while (length > 0) {
+            if (Character.isSpace(text[start])) {
+                start++;
+                length--;
+            }
+        }
+        int newlen = 0;
+        for (; newlen < length; newlen++) {
+            final char c = text[newlen];
+            if (c == '\n' || c == '\r') {
+                break;
+            }
+        }
+        return new String(text, start, newlen);
+    }
+
+    /**
+     * If the pattern matches, return the first group of that as an Integer.
+     * If not return null.
+     */
+    private static Integer matchIndexedRegex(Locator locator, Pattern pattern, String text)
+            throws SAXParseException {
+        final Matcher m = pattern.matcher(text);
+        if (m.matches()) {
+            try {
+                return Integer.parseInt(m.group(1));
+            } catch (NumberFormatException ex) {
+                throw new SAXParseException("Invalid field name: '" + text + "'", locator, ex);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public static PowerProfile parse(InputStream stream) throws ParseException {
+        return (new Parser(stream)).parse();
+    }
+
+    private PowerProfile() {
+    }
+
+    public ComponentProfile getComponent(Component component) {
+        return mComponents.get(component);
+    }
+
+}
diff --git a/tools/powermodel/src/com/android/powermodel/PowerReport.java b/tools/powermodel/src/com/android/powermodel/PowerReport.java
new file mode 100644
index 0000000..76ba672
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/PowerReport.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * PowerReport contains the summary of all power used on a device
+ * as reported by batterystats or statsd, based on the power profile.
+ */
+public class PowerReport {
+    private AppList<AppPower> mApps;
+    private double mTotalPowerMah;
+
+    private PowerReport() {
+    }
+
+    /**
+     * The total power used by this device for this PowerReport.
+     */
+    public double getTotalPowerMah() {
+        return mTotalPowerMah;
+    }
+
+    public List<AppPower> getAllApps() {
+        return mApps.getAllApps();
+    }
+
+    public List<AppPower> getRegularApps() {
+        return mApps.getRegularApps();
+    }
+
+    public List<AppPower> findApp(String pkg) {
+        return mApps.findApp(pkg);
+    }
+
+    public AppPower findApp(SpecialApp specialApp) {
+        return mApps.findApp(specialApp);
+    }
+
+    public static PowerReport createReport(PowerProfile profile, ActivityReport activityReport) {
+        final PowerReport.Builder powerReport = new PowerReport.Builder();
+        for (final AppActivity appActivity: activityReport.getAllApps()) {
+            final AppPower.Builder appPower = new AppPower.Builder();
+            appPower.setAttribution(appActivity.getAttribution());
+
+            for (final ImmutableMap.Entry<Component,ComponentActivity> entry:
+                    appActivity.getComponentActivities().entrySet()) {
+                final ComponentPower componentPower = entry.getValue()
+                        .applyProfile(activityReport, profile);
+                if (componentPower != null) {
+                    appPower.addComponentPower(entry.getKey(), componentPower);
+                }
+            }
+
+            powerReport.add(appPower);
+        }
+        return powerReport.build();
+    }
+
+    private static class Builder {
+        private AppList.Builder mApps = new AppList.Builder();
+
+        public Builder() {
+        }
+
+        public PowerReport build() {
+            final PowerReport report = new PowerReport();
+
+            report.mApps = mApps.build();
+
+            for (AppPower app: report.mApps.getAllApps()) {
+                report.mTotalPowerMah += app.getAppPowerMah();
+            }
+
+            return report;
+        }
+
+        public void add(AppPower.Builder app) {
+            mApps.put(app.getAttribution(), app);
+        }
+    }
+}
diff --git a/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
new file mode 100644
index 0000000..76c0482
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/RawBatteryStats.java
@@ -0,0 +1,1175 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class RawBatteryStats {
+    /**
+     * The factory objects for the records. Initialized in the static block.
+     */
+    private static HashMap<String,RecordFactory> sFactories
+            = new HashMap<String,RecordFactory>();
+
+    /**
+     * The Record objects that have been parsed.
+     */
+    private ArrayList<Record> mRecords = new ArrayList<Record>();
+
+    /**
+     * The Record objects that have been parsed, indexed by type.
+     *
+     * Don't use this before {@link #indexRecords()} has been called.
+     */
+    private ImmutableMap<String,ImmutableList<Record>> mRecordsByType;
+
+    /**
+     * The attribution keys for which we have data (corresponding to UIDs we've seen).
+     * <p>
+     * Does not include the synthetic apps.
+     * <p>
+     * Don't use this before {@link #indexRecords()} has been called.
+     */
+    private ImmutableSet<AttributionKey> mApps;
+
+    /**
+     * The warnings that have been issued during parsing.
+     */
+    private ArrayList<Warning> mWarnings = new ArrayList<Warning>();
+
+    /**
+     * The version of the BatteryStats dumpsys that we are using.  This value
+     * is set to -1 initially, and then when parsing the (hopefully) first
+     * line, 'vers', it is set to the correct version.
+     */
+    private int mDumpsysVersion = -1;
+
+    /**
+     * Enum used in the Line annotation to mark whether a field is expected to be
+     * system-wide or scoped to an app.
+     */
+    public enum Scope {
+        SYSTEM,
+        UID
+    }
+
+    /**
+     * Enum used to indicated the expected number of results.
+     */
+    public enum Count {
+        SINGLE,
+        MULTIPLE
+    }
+
+    /**
+     * Annotates classes that represent a line of CSV in the batterystats CSV
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    @interface Line {
+        String tag();
+        Scope scope();
+        Count count();
+    }
+
+    /**
+     * Annotates fields that should be parsed automatically from CSV
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    @interface Field {
+        /**
+         * The "column" of this field in the most recent version of the CSV.
+         * When parsing old versions, fields that were added will be automatically
+         * removed and the indices will be fixed up.
+         *
+         * The header fields (version, uid, category, type) will be automatically
+         * handled for the base Line type.  The index 0 should start after those.
+         */
+        int index();
+
+        /**
+         * First version that this field appears in.
+         */
+        int added() default 0;
+    }
+
+    /**
+     * Each line in the BatteryStats CSV is tagged with a category, that says
+     * which of the time collection modes was used for the data.
+     */
+    public enum Category {
+        INFO("i"),
+        LAST("l"),
+        UNPLUGGED("u"),
+        CURRENT("c");
+
+        public final String tag;
+
+        Category(String tag) {
+            this.tag = tag;
+        }
+    }
+
+    /**
+     * Base class for all lines in a batterystats CSV file.
+     */
+    public static class Record {
+        /**
+         * Whether all of the fields for the indicated version of this record
+         * have been filled in.
+         */
+        public boolean complete;
+
+
+        @Field(index=-4)
+        public int lineVersion;
+
+        @Field(index=-3)
+        public int uid;
+
+        @Field(index=-2)
+        public Category category;
+
+        @Field(index=-1)
+        public String lineType;
+    }
+
+    @Line(tag="bt", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class Battery extends Record {
+        // If which != STATS_SINCE_CHARGED, the csv will be "N/A" and we will get
+        // a parsing warning.  Nobody uses anything other than STATS_SINCE_CHARGED.
+        @Field(index=0)
+        public int startCount;
+
+        @Field(index=1)
+        public long whichBatteryRealtimeMs;
+
+        @Field(index=2)
+        public long whichBatteryUptimeMs;
+
+        @Field(index=3)
+        public long totalRealtimeMs;
+
+        @Field(index=4)
+        public long totalUptimeMs;
+
+        @Field(index=5)
+        public long getStartClockTimeMs;
+
+        @Field(index=6)
+        public long whichBatteryScreenOffRealtimeMs;
+
+        @Field(index=7)
+        public long whichBatteryScreenOffUptimeMs;
+
+        @Field(index=8)
+        public long estimatedBatteryCapacityMah;
+
+        @Field(index=9)
+        public long minLearnedBatteryCapacityMah;
+
+        @Field(index=10)
+        public long maxLearnedBatteryCapacityMah;
+
+        @Field(index=11)
+        public long screenDozeTimeMs;
+    }
+
+    @Line(tag="gn", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class GlobalNetwork extends Record {
+        @Field(index=0)
+        public long mobileRxTotalBytes;
+
+        @Field(index=1)
+        public long mobileTxTotalBytes;
+
+        @Field(index=2)
+        public long wifiRxTotalBytes;
+
+        @Field(index=3)
+        public long wifiTxTotalBytes;
+
+        @Field(index=4)
+        public long mobileRxTotalPackets;
+
+        @Field(index=5)
+        public long mobileTxTotalPackets;
+
+        @Field(index=6)
+        public long wifiRxTotalPackets;
+
+        @Field(index=7)
+        public long wifiTxTotalPackets;
+
+        @Field(index=8)
+        public long btRxTotalBytes;
+
+        @Field(index=9)
+        public long btTxTotalBytes;
+    }
+
+    @Line(tag="gmcd", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class GlobalModemController extends Record {
+        @Field(index=0)
+        public long idleMs;
+
+        @Field(index=1)
+        public long rxTimeMs;
+
+        @Field(index=2)
+        public long powerMaMs;
+
+        @Field(index=3)
+        public long[] txTimeMs;
+    }
+
+    @Line(tag="m", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class Misc extends Record {
+        @Field(index=0)
+        public long screenOnTimeMs;
+
+        @Field(index=1)
+        public long phoneOnTimeMs;
+
+        @Field(index=2)
+        public long fullWakeLockTimeTotalMs;
+
+        @Field(index=3)
+        public long partialWakeLockTimeTotalMs;
+
+        @Field(index=4)
+        public long mobileRadioActiveTimeMs;
+
+        @Field(index=5)
+        public long mobileRadioActiveAdjustedTimeMs;
+
+        @Field(index=6)
+        public long interactiveTimeMs;
+
+        @Field(index=7)
+        public long powerSaveModeEnabledTimeMs;
+
+        @Field(index=8)
+        public int connectivityChangeCount;
+
+        @Field(index=9)
+        public long deepDeviceIdleModeTimeMs;
+
+        @Field(index=10)
+        public long deepDeviceIdleModeCount;
+
+        @Field(index=11)
+        public long deepDeviceIdlingTimeMs;
+
+        @Field(index=12)
+        public long deepDeviceIdlingCount;
+
+        @Field(index=13)
+        public long mobileRadioActiveCount;
+
+        @Field(index=14)
+        public long mobileRadioActiveUnknownTimeMs;
+
+        @Field(index=15)
+        public long lightDeviceIdleModeTimeMs;
+
+        @Field(index=16)
+        public long lightDeviceIdleModeCount;
+
+        @Field(index=17)
+        public long lightDeviceIdlingTimeMs;
+
+        @Field(index=18)
+        public long lightDeviceIdlingCount;
+
+        @Field(index=19)
+        public long lightLongestDeviceIdleModeTimeMs;
+
+        @Field(index=20)
+        public long deepLongestDeviceIdleModeTimeMs;
+    }
+
+    @Line(tag="nt", scope=Scope.UID, count=Count.SINGLE)
+    public static class Network extends Record {
+        @Field(index=0)
+        public long mobileRxBytes;
+
+        @Field(index=1)
+        public long mobileTxBytes;
+
+        @Field(index=2)
+        public long wifiRxBytes;
+
+        @Field(index=3)
+        public long wifiTxBytes;
+
+        @Field(index=4)
+        public long mobileRxPackets;
+
+        @Field(index=5)
+        public long mobileTxPackets;
+
+        @Field(index=6)
+        public long wifiRxPackets;
+
+        @Field(index=7)
+        public long wifiTxPackets;
+
+        // This is microseconds, because... batterystats.
+        @Field(index=8)
+        public long mobileRadioActiveTimeUs;
+
+        @Field(index=9)
+        public long mobileRadioActiveCount;
+
+        @Field(index=10)
+        public long btRxBytes;
+
+        @Field(index=11)
+        public long btTxBytes;
+
+        @Field(index=12)
+        public long mobileWakeupCount;
+
+        @Field(index=13)
+        public long wifiWakeupCount;
+
+        @Field(index=14)
+        public long mobileBgRxBytes;
+
+        @Field(index=15)
+        public long mobileBgTxBytes;
+
+        @Field(index=16)
+        public long wifiBgRxBytes;
+
+        @Field(index=17)
+        public long wifiBgTxBytes;
+
+        @Field(index=18)
+        public long mobileBgRxPackets;
+
+        @Field(index=19)
+        public long mobileBgTxPackets;
+
+        @Field(index=20)
+        public long wifiBgRxPackets;
+
+        @Field(index=21)
+        public long wifiBgTxPackets;
+    }
+
+    @Line(tag="sgt", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class SignalStrengthTime extends Record {
+        @Field(index=0)
+        public long[] phoneSignalStrengthTimeMs;
+    }
+
+    @Line(tag="sst", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class SignalScanningTime extends Record {
+        @Field(index=0)
+        public long phoneSignalScanningTimeMs;
+    }
+
+    @Line(tag="uid", scope=Scope.UID, count=Count.MULTIPLE)
+    public static class Uid extends Record {
+        @Field(index=0)
+        public int uidKey;
+
+        @Field(index=1)
+        public String pkg;
+    }
+
+    @Line(tag="vers", scope=Scope.SYSTEM, count=Count.SINGLE)
+    public static class Version extends Record {
+        @Field(index=0)
+        public int dumpsysVersion;
+
+        @Field(index=1)
+        public int parcelVersion;
+
+        @Field(index=2)
+        public String startPlatformVersion;
+
+        @Field(index=3)
+        public String endPlatformVersion;
+    }
+
+    /**
+     * Codes for the warnings to classify warnings without parsing them.
+     */
+    public enum WarningId {
+        /**
+         * A row didn't have enough fields to match any records and let us extract
+         * a line type.
+         */
+        TOO_FEW_FIELDS_FOR_LINE_TYPE,
+
+        /**
+         * We couldn't find a Record for the given line type.
+         */
+        NO_MATCHING_LINE_TYPE,
+
+        /**
+         * Couldn't set the value of a field. Usually this is because the
+         * contents of a numeric type couldn't be parsed.
+         */
+        BAD_FIELD_TYPE,
+
+        /**
+         * There were extra field values in the input text.
+         */
+        TOO_MANY_FIELDS,
+
+        /**
+         * There were fields that we were expecting (for this version
+         * of the dumpsys) that weren't provided in the CSV data.
+         */
+        NOT_ENOUGH_FIELDS,
+
+        /**
+         * The dumpsys version in the 'vers' CSV line couldn't be parsed.
+         */
+        BAD_DUMPSYS_VERSION
+    }
+
+    /**
+     * A non-fatal problem detected during parsing.
+     */
+    public static class Warning {
+        private int mLineNumber;
+        private WarningId mId;
+        private ArrayList<String> mFields;
+        private String mMessage;
+        private String[] mExtras;
+
+        public Warning(int lineNumber, WarningId id, ArrayList<String> fields, String message,
+                String[] extras) {
+            mLineNumber = lineNumber;
+            mId = id;
+            mFields = fields;
+            mMessage = message;
+            mExtras = extras;
+        }
+
+        public int getLineNumber() {
+            return mLineNumber;
+        }
+
+        public ArrayList<String> getFields() {
+            return mFields;
+        }
+
+        public String getMessage() {
+            return mMessage;
+        }
+
+        public String[] getExtras() {
+            return mExtras;
+        }
+    }
+
+    /**
+     * Base class for classes to set fields on Record objects via reflection.
+     */
+    private abstract static class FieldSetter {
+        private int mIndex;
+        private int mAdded;
+        protected java.lang.reflect.Field mField;
+
+        FieldSetter(int index, int added, java.lang.reflect.Field field) {
+            mIndex = index;
+            mAdded = added;
+            mField = field;
+        }
+
+        String getName() {
+            return mField.getName();
+        }
+
+        int getIndex() {
+            return mIndex;
+        }
+
+        int getAdded() {
+            return mAdded;
+        }
+
+        boolean isArray() {
+            return mField.getType().isArray();
+        }
+
+        abstract void setField(int lineNumber, Record object, String value) throws ParseException;
+        abstract void setArray(int lineNumber, Record object, ArrayList<String> values,
+                int startIndex, int endIndex) throws ParseException;
+
+        @Override
+        public String toString() {
+            final String className = getClass().getName();
+            int startIndex = Math.max(className.lastIndexOf('.'), className.lastIndexOf('$'));
+            if (startIndex < 0) {
+                startIndex = 0;
+            } else {
+                startIndex++;
+            }
+            return className.substring(startIndex) + "(index=" + mIndex + " added=" + mAdded
+                    + " field=" + mField.getName()
+                    + " type=" + mField.getType().getSimpleName()
+                    + ")";
+        }
+    }
+
+    /**
+     * Sets int fields on Record objects using reflection.
+     */
+    private static class IntFieldSetter extends FieldSetter {
+        IntFieldSetter(int index, int added, java.lang.reflect.Field field) {
+            super(index, added, field);
+        }
+
+        void setField(int lineNumber, Record object, String value) throws ParseException {
+            try {
+                mField.setInt(object, Integer.parseInt(value.trim()));
+            } catch (NumberFormatException ex) {
+                throw new ParseException(lineNumber, "can't parse as integer: " + value);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        void setArray(int lineNumber, Record object, ArrayList<String> values,
+                int startIndex, int endIndex) throws ParseException {
+            try {
+                final int[] array = new int[endIndex-startIndex];
+                for (int i=startIndex; i<endIndex; i++) {
+                    final String value = values.get(startIndex+i);
+                    try {
+                        array[i] = Integer.parseInt(value.trim());
+                    } catch (NumberFormatException ex) {
+                        throw new ParseException(lineNumber, "can't parse field "
+                                + i + " as integer: " + value);
+                    }
+                }
+                mField.set(object, array);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Sets long fields on Record objects using reflection.
+     */
+    private static class LongFieldSetter extends FieldSetter {
+        LongFieldSetter(int index, int added, java.lang.reflect.Field field) {
+            super(index, added, field);
+        }
+
+        void setField(int lineNumber, Record object, String value) throws ParseException {
+            try {
+                mField.setLong(object, Long.parseLong(value.trim()));
+            } catch (NumberFormatException ex) {
+                throw new ParseException(lineNumber, "can't parse as long: " + value);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        void setArray(int lineNumber, Record object, ArrayList<String> values,
+                int startIndex, int endIndex) throws ParseException {
+            try {
+                final long[] array = new long[endIndex-startIndex];
+                for (int i=0; i<(endIndex-startIndex); i++) {
+                    final String value = values.get(startIndex+i);
+                    try {
+                        array[i] = Long.parseLong(value.trim());
+                    } catch (NumberFormatException ex) {
+                        throw new ParseException(lineNumber, "can't parse field "
+                                + i + " as long: " + value);
+                    }
+                }
+                mField.set(object, array);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Sets String fields on Record objects using reflection.
+     */
+    private static class StringFieldSetter extends FieldSetter {
+        StringFieldSetter(int index, int added, java.lang.reflect.Field field) {
+            super(index, added, field);
+        }
+
+        void setField(int lineNumber, Record object, String value) throws ParseException {
+            try {
+                mField.set(object, value);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        void setArray(int lineNumber, Record object, ArrayList<String> values,
+                int startIndex, int endIndex) throws ParseException {
+            try {
+                final String[] array = new String[endIndex-startIndex];
+                for (int i=0; i<(endIndex-startIndex); i++) {
+                    array[i] = values.get(startIndex+1);
+                }
+                mField.set(object, array);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Sets enum fields on Record objects using reflection.
+     *
+     * To be parsed automatically, enums must have a public final String tag
+     * field, which is the string that will appear in the csv for that enum value.
+     */
+    private static class EnumFieldSetter extends FieldSetter {
+        private final HashMap<String,Enum> mTags = new HashMap<String,Enum>();
+
+        EnumFieldSetter(int index, int added, java.lang.reflect.Field field) {
+            super(index, added, field);
+
+            // Build the mapping of tags to values.
+            final Class<?> fieldType = field.getType();
+
+            java.lang.reflect.Field tagField = null;
+            try {
+                tagField = fieldType.getField("tag");
+            } catch (NoSuchFieldException ex) {
+                throw new RuntimeException("Missing tag field."
+                        + " To be parsed automatically, enums must have"
+                        + " a String field called tag.  Enum class: " + fieldType.getName()
+                        + " Containing class: " + field.getDeclaringClass().getName()
+                        + " Field: " + field.getName());
+
+            }
+            if (!String.class.equals(tagField.getType())) {
+                throw new RuntimeException("Tag field is not string."
+                        + " To be parsed automatically, enums must have"
+                        + " a String field called tag.  Enum class: " + fieldType.getName()
+                        + " Containing class: " + field.getDeclaringClass().getName()
+                        + " Field: " + field.getName()
+                        + " Tag field type: " + tagField.getType().getName());
+            }
+
+            for (final Object enumValue: fieldType.getEnumConstants()) {
+                String tag = null;
+                try {
+                    tag = (String)tagField.get(enumValue);
+                } catch (IllegalAccessException | IllegalArgumentException
+                        | ExceptionInInitializerError ex) {
+                    throw new RuntimeException(ex);
+                }
+                mTags.put(tag, (Enum)enumValue);
+            }
+        }
+
+        void setField(int lineNumber, Record object, String value) throws ParseException {
+            final Enum enumValue = mTags.get(value);
+            if (enumValue == null) {
+                throw new ParseException(lineNumber, "Could not find enum for field "
+                        + getName() + " for tag: " + value);
+            }
+            try {
+                mField.set(object, enumValue);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        void setArray(int lineNumber, Record object, ArrayList<String> values,
+                int startIndex, int endIndex) throws ParseException {
+            try {
+                final Object array = Array.newInstance(mField.getType().getComponentType(),
+                        endIndex-startIndex);
+                for (int i=0; i<(endIndex-startIndex); i++) {
+                    final String value = values.get(startIndex+i);
+                    final Enum enumValue = mTags.get(value);
+                    if (enumValue == null) {
+                        throw new ParseException(lineNumber, "Could not find enum for field "
+                                + getName() + " for tag: " + value);
+                    }
+                    Array.set(array, i, enumValue);
+                }
+                mField.set(object, array);
+            } catch (IllegalAccessException | IllegalArgumentException
+                    | ExceptionInInitializerError ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+    /**
+     * Factory for the record classes.  Uses reflection to create
+     * the fields.
+     */
+    private static class RecordFactory {
+        private String mTag;
+        private Class<? extends Record> mSubclass;
+        private ArrayList<FieldSetter> mFieldSetters;
+
+        RecordFactory(String tag, Class<? extends Record> subclass,
+                ArrayList<FieldSetter> fieldSetters) {
+            mTag = tag;
+            mSubclass = subclass;
+            mFieldSetters = fieldSetters;
+        }
+
+        /**
+         * Create an object of one of the subclasses of Record, and fill
+         * in the fields marked with the Field annotation.
+         *
+         * @return a new Record with the fields filled in. If there are missing
+         *      fields, the {@link Record.complete} field will be set to false.
+         */
+        Record create(RawBatteryStats bs, int dumpsysVersion, int lineNumber,
+                ArrayList<String> fieldValues) {
+            final boolean debug = false;
+            Record record = null;
+            try {
+                if (debug) {
+                    System.err.println("Creating object: " + mSubclass.getSimpleName());
+                }
+                record = mSubclass.newInstance();
+            } catch (IllegalAccessException | InstantiationException
+                    | ExceptionInInitializerError | SecurityException ex) {
+                throw new RuntimeException("Exception creating " + mSubclass.getName()
+                        + " for '" + mTag + "' record.", ex);
+            }
+            record.complete = true;
+            int fieldIndex = 0;
+            int setterIndex = 0;
+            while (fieldIndex < fieldValues.size() && setterIndex < mFieldSetters.size()) {
+                final FieldSetter setter = mFieldSetters.get(setterIndex);
+
+                if (dumpsysVersion >= 0 && dumpsysVersion < setter.getAdded()) {
+                    // The version being parsed doesn't have the field for this setter,
+                    // so skip the setter but not the field.
+                    setterIndex++;
+                    continue;
+                }
+
+                final String value = fieldValues.get(fieldIndex);
+                try {
+                    if (debug) {
+                        System.err.println("   setting field " + setter + " to: " + value);
+                    }
+                    if (setter.isArray()) {
+                        setter.setArray(lineNumber, record, fieldValues,
+                                fieldIndex, fieldValues.size());
+                        // The rest of the fields have been consumed.
+                        fieldIndex = fieldValues.size();
+                        setterIndex = mFieldSetters.size();
+                        break;
+                    } else {
+                        setter.setField(lineNumber, record, value);
+                    }
+                } catch (ParseException ex) {
+                    bs.addWarning(lineNumber, WarningId.BAD_FIELD_TYPE, fieldValues,
+                            ex.getMessage(), mTag, value);
+                    record.complete = false;
+                }
+
+                fieldIndex++;
+                setterIndex++;
+            }
+
+            // If there are extra fields, this record is complete, there are just
+            // extra values, so we issue a warning but don't mark it incomplete.
+            if (fieldIndex < fieldValues.size()) {
+                bs.addWarning(lineNumber, WarningId.TOO_MANY_FIELDS, fieldValues,
+                        "Line '" + mTag + "' has extra fields.",
+                        mTag, Integer.toString(fieldIndex), Integer.toString(fieldValues.size()));
+                if (debug) {
+                    for (int i=0; i<mFieldSetters.size(); i++) {
+                        System.err.println("    setter: [" + i + "] " + mFieldSetters.get(i));
+                    }
+                }
+            }
+
+            // If we have any fields that are missing, add a warning and return null.
+            for (; setterIndex < mFieldSetters.size(); setterIndex++) {
+                final FieldSetter setter = mFieldSetters.get(setterIndex);
+                if (dumpsysVersion >= 0 && dumpsysVersion >= setter.getAdded()) {
+                    bs.addWarning(lineNumber, WarningId.NOT_ENOUGH_FIELDS, fieldValues,
+                            "Line '" + mTag + "' missing field: index=" + setterIndex
+                                + " name=" + setter.getName(),
+                            mTag, Integer.toString(setterIndex));
+                    record.complete = false;
+                }
+            }
+
+            return record;
+        }
+    }
+
+    /**
+     * Parse the input stream and return a RawBatteryStats object.
+     */
+    public static RawBatteryStats parse(InputStream input) throws ParseException, IOException {
+        final RawBatteryStats result = new RawBatteryStats();
+        result.parseImpl(input);
+        return result;
+    }
+
+    /**
+     * Get a record.
+     * <p>
+     * If multiple of that record are found, returns the first one.  There will already
+     * have been a warning recorded if the count annotation did not match what was in the
+     * csv.
+     * <p>
+     * Returns null if there are no records of that type.
+     */
+    public <T extends Record> T getSingle(Class<T> cl) {
+        final List<Record> list = mRecordsByType.get(cl.getName());
+        if (list == null) {
+            return null;
+        }
+        // Notes:
+        //   - List can never be empty because the list itself wouldn't have been added.
+        //   - Cast is safe because list was populated based on class name (let's assume
+        //     there's only one class loader involved here).
+        return (T)list.get(0);
+    }
+
+    /**
+     * Get a record.
+     * <p>
+     * If multiple of that record are found, returns the first one that matches that uid.
+     * <p>
+     * Returns null if there are no records of that type that match the given uid.
+     */
+    public <T extends Record> T getSingle(Class<T> cl, int uid) {
+        final List<Record> list = mRecordsByType.get(cl.getName());
+        if (list == null) {
+            return null;
+        }
+        for (final Record record: list) {
+            if (record.uid == uid) {
+                // Cast is safe because list was populated based on class name (let's assume
+                // there's only one class loader involved here).
+                return (T)record;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get all the records of the given type.
+     */
+    public <T extends Record> List<T> getMultiple(Class<T> cl) {
+        final List<Record> list = mRecordsByType.get(cl.getName());
+        if (list == null) {
+            return ImmutableList.<T>of();
+        }
+        // Cast is safe because list was populated based on class name (let's assume
+        // there's only one class loader involved here).
+        return ImmutableList.copyOf((List<T>)list);
+    }
+
+    /**
+     * Get the UIDs that are covered by this batterystats dump.
+     */
+    public Set<AttributionKey> getApps() {
+        return mApps;
+    }
+
+    /**
+     * No public constructor. Use {@link #parse}.
+     */
+    private RawBatteryStats() {
+    }
+
+    /**
+     * Get the list of Record objects that were parsed from the csv.
+     */
+    public List<Record> getRecords() {
+        return mRecords;
+    }
+
+    /**
+     * Gets the warnings that were encountered during parsing.
+     */
+    public List<Warning> getWarnings() {
+        return mWarnings;
+    }
+
+    /**
+     * Implementation of the csv parsing.
+     */
+    private void parseImpl(InputStream input) throws ParseException, IOException {
+        // Parse the csv
+        CsvParser.parse(input, new CsvParser.LineProcessor() {
+                    @Override
+                    public void onLine(int lineNumber, ArrayList<String> fields)
+                            throws ParseException {
+                        handleCsvLine(lineNumber, fields);
+                    }
+                });
+
+        // Gather the records by class name for the getSingle() and getMultiple() functions.
+        indexRecords();
+
+        // Gather the uids from all the places UIDs come from, for getApps().
+        indexApps();
+    }
+
+    /**
+     * Handle a line of CSV input, creating the right Record object.
+     */
+    private void handleCsvLine(int lineNumber, ArrayList<String> fields) throws ParseException {
+        // The standard rows all have the 4 core fields. Anything less isn't what we're
+        // looking for.
+        if (fields.size() <= 4) {
+            addWarning(lineNumber, WarningId.TOO_FEW_FIELDS_FOR_LINE_TYPE, fields,
+                    "Line with too few fields (" + fields.size() + ")",
+                    Integer.toString(fields.size()));
+            return;
+        }
+
+        final String lineType = fields.get(3);
+
+        // Handle the vers line specially, because we need the version number
+        // to make the rest of the machinery work.
+        if ("vers".equals(lineType)) {
+            final String versionText = fields.get(4);
+            try {
+                mDumpsysVersion = Integer.parseInt(versionText);
+            } catch (NumberFormatException ex) {
+                addWarning(lineNumber, WarningId.BAD_DUMPSYS_VERSION, fields,
+                        "Couldn't parse dumpsys version number: '" + versionText,
+                        versionText);
+            }
+        }
+
+        // Find the right factory.
+        final RecordFactory factory = sFactories.get(lineType);
+        if (factory == null) {
+            addWarning(lineNumber, WarningId.NO_MATCHING_LINE_TYPE, fields,
+                    "No Record for line type '" + lineType + "'",
+                    lineType);
+            return;
+        }
+
+        // Create the record.
+        final Record record = factory.create(this, mDumpsysVersion, lineNumber, fields);
+        mRecords.add(record);
+    }
+
+    /**
+     * Add to the list of warnings.
+     */
+    private void addWarning(int lineNumber, WarningId id,
+            ArrayList<String> fields, String message, String... extras) {
+        mWarnings.add(new Warning(lineNumber, id, fields, message, extras));
+        final boolean debug = false;
+        if (debug) {
+            final StringBuilder text = new StringBuilder("line ");
+            text.append(lineNumber);
+            text.append(": WARNING: ");
+            text.append(message);
+            text.append("\n    fields: ");
+            for (int i=0; i<fields.size(); i++) {
+                final String field = fields.get(i);
+                if (field.indexOf('"') >= 0) {
+                    text.append('"');
+                    text.append(field.replace("\"", "\"\""));
+                    text.append('"');
+                } else {
+                    text.append(field);
+                }
+                if (i != fields.size() - 1) {
+                    text.append(',');
+                }
+            }
+            text.append('\n');
+            for (String extra: extras) {
+                text.append("    extra: ");
+                text.append(extra);
+                text.append('\n');
+            }
+            System.err.print(text.toString());
+        }
+    }
+
+    /**
+     * Group records by class name.
+     */
+    private void indexRecords() {
+        final HashMap<String,ArrayList<Record>> map = new HashMap<String,ArrayList<Record>>();
+
+        // Iterate over all of the records
+        for (Record record: mRecords) {
+            final String className = record.getClass().getName();
+
+            ArrayList<Record> list = map.get(className);
+            if (list == null) {
+                list = new ArrayList<Record>();
+                map.put(className, list);
+            }
+
+            list.add(record);
+        }
+
+        // Make it immutable
+        final HashMap<String,ImmutableList<Record>> result
+                = new HashMap<String,ImmutableList<Record>>();
+        for (HashMap.Entry<String,ArrayList<Record>> entry: map.entrySet()) {
+            result.put(entry.getKey(), ImmutableList.copyOf(entry.getValue()));
+        }
+
+        // Initialize here so uninitialized access will result in NPE.
+        mRecordsByType = ImmutableMap.copyOf(result);
+    }
+
+    /**
+     * Collect the UIDs from the csv.
+     *
+     * They come from two places.
+     * <ul>
+     *   <li>The uid to package name map entries ({@link #Uid}) at the beginning.
+     *   <li>The uid fields of the rest of the entries, some of which might not
+     *       have package names associated with them.
+     * </ul>
+     *
+     * TODO: Is this where we should also do the logic about the special UIDs?
+     */
+    private void indexApps() {
+        final HashMap<Integer,HashSet<String>> uids = new HashMap<Integer,HashSet<String>>();
+
+        // The Uid rows, from which we get package names
+        for (Uid record: getMultiple(Uid.class)) {
+            HashSet<String> list = uids.get(record.uidKey);
+            if (list == null) {
+                list = new HashSet<String>();
+                uids.put(record.uidKey, list);
+            }
+            list.add(record.pkg);
+        }
+
+        // The uid fields of everything
+        for (Record record: mRecords) {
+            // The 0 in the INFO records isn't really root, it's just unfilled data.
+            // The root uid (0) will show up practically in every record, but don't force it.
+            if (record.category != Category.INFO) {
+                if (uids.get(record.uid) == null) {
+                    // There is no other data about this UID, but it does exist!
+                    uids.put(record.uid, new HashSet<String>());
+                }
+            }
+        }
+
+        // Turn our temporary lists of package names into AttributionKeys.
+        final HashSet<AttributionKey> result = new HashSet<AttributionKey>();
+        for (HashMap.Entry<Integer,HashSet<String>> entry: uids.entrySet()) {
+            result.add(new AttributionKey(entry.getKey(), entry.getValue()));
+        }
+
+        // Initialize here so uninitialized access will result in NPE.
+        mApps = ImmutableSet.copyOf(result);
+    }
+
+    /**
+     * Init the factory classes.
+     */
+    static {
+        for (Class<?> cl: RawBatteryStats.class.getClasses()) {
+            final Line lineAnnotation = cl.getAnnotation(Line.class);
+            if (lineAnnotation != null && Record.class.isAssignableFrom(cl)) {
+                final ArrayList<FieldSetter> fieldSetters = new ArrayList<FieldSetter>();
+
+                for (java.lang.reflect.Field field: cl.getFields()) {
+                    final Field fa = field.getAnnotation(Field.class);
+                    if (fa != null) {
+                        final Class<?> fieldType = field.getType();
+                        final Class<?> innerType = fieldType.isArray()
+                                ? fieldType.getComponentType()
+                                : fieldType;
+                        if (Integer.TYPE.equals(innerType)) {
+                            fieldSetters.add(new IntFieldSetter(fa.index(), fa.added(), field));
+                        } else if (Long.TYPE.equals(innerType)) {
+                            fieldSetters.add(new LongFieldSetter(fa.index(), fa.added(), field));
+                        } else if (String.class.equals(innerType)) {
+                            fieldSetters.add(new StringFieldSetter(fa.index(), fa.added(), field));
+                        } else if (innerType.isEnum()) {
+                            fieldSetters.add(new EnumFieldSetter(fa.index(), fa.added(), field));
+                        } else {
+                            throw new RuntimeException("Unsupported field type '"
+                                    + fieldType.getName() + "' on "
+                                    + cl.getName() + "." + field.getName());
+                        }
+                    }
+                }
+                // Sort by index
+                Collections.sort(fieldSetters, new Comparator<FieldSetter>() {
+                            @Override
+                            public int compare(FieldSetter a, FieldSetter b) {
+                                return a.getIndex() - b.getIndex();
+                            }
+                        });
+                // Only the last one can be an array
+                for (int i=0; i<fieldSetters.size()-1; i++) {
+                    if (fieldSetters.get(i).isArray()) {
+                        throw new RuntimeException("Only the last (highest index) @Field"
+                                + " in class " + cl.getName() + " can be an array: "
+                                + fieldSetters.get(i).getName());
+                    }
+                }
+                // Add to the map
+                sFactories.put(lineAnnotation.tag(), new RecordFactory(lineAnnotation.tag(),
+                            (Class<Record>)cl, fieldSetters));
+            }
+        }
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/SpecialApp.java b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
new file mode 100644
index 0000000..df1e1fb
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/SpecialApp.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+/**
+ * Identifiers for well-known apps that have unique characteristics.
+ *
+ * @more
+ * This includes three categories:
+ * <ul>
+ *   <li><b>Built-in system components</b> – These have predefined UIDs that are
+ *   always the same. For example, the system UID is always 1000.</li>
+ *   <li><b>Well known apps with shared UIDs</b> – These do not have predefined
+ *   UIDs (i.e. are different on each device), but since they have shared UIDs
+ *   with varying sets of package names (GmsCore is the canonical example), we
+ *   have special logic to capture these into a single entity with a well defined
+ *   key. These have the {@link #uid uid} field set to
+ *   {@link Uid#UID_VARIES Uid.UID_VARIES}.</li>
+ *   <li><b>Synthetic remainder app</b> – The {@link #REMAINDER REMAINDER} app doesn't
+ *   represent a real app. It contains accounting for usage which is not attributed
+ *   to any UID. This app has the {@link #uid uid} field set to
+ *   {@link Uid#UID_SYNTHETIC Uid.UID_SYNTHETIC}.</li>
+ * </ul>
+ */
+public enum SpecialApp {
+
+    /**
+     * Synthetic app that accounts for the remaining amount of resources used
+     * that is unaccounted for by apps, or overcounted because of inaccuracies
+     * in the model.
+     */
+    REMAINDER(Uid.UID_SYNTHETIC),
+
+    /**
+     * Synthetic app that holds system-wide numbers, for example the total amount
+     * of various resources used, device-wide.
+     */
+    GLOBAL(Uid.UID_SYNTHETIC),
+
+    SYSTEM(1000),
+
+    GOOGLE_SERVICES(Uid.UID_VARIES);
+
+    /**
+     * Constants for SpecialApps where the uid is not actually a UID.
+     */
+    public static class Uid {
+        /**
+         * Constant to indicate that this special app does not have a fixed UID.
+         */
+        public static final int UID_VARIES = -1;
+
+        /**
+         * Constant to indicate that this special app is not actually an app with a UID.
+         * 
+         * @see SpecialApp#REMAINDER
+         * @see SpecialApp#GLOBAL
+         */
+        public static final int UID_SYNTHETIC = -2;
+    }
+
+    /**
+     * The fixed UID value of this special app, or {@link #UID_VARIES} if there
+     * isn't one.
+     */
+    public final int uid;
+
+    private SpecialApp(int uid) {
+        this.uid = uid;
+    }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
index 27d25b8..63ff3a6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/AudioProfile.java
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class AudioProfile extends ComponentProfile {
+    public float onMa;
 }
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
index 27d25b8..8f5e7d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/BluetoothProfile.java
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class BluetoothProfile extends ComponentProfile {
+    public float idleMa;
+    public float rxMa;
+    public float txMa;
 }
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
index 27d25b8..8ee22d0 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/CameraProfile.java
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CameraProfile extends ComponentProfile {
+    public float onMa;
 }
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
new file mode 100644
index 0000000..0b34fc8
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/CpuProfile.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class CpuProfile extends ComponentProfile {
+    public float suspendMa;
+    public float idleMa;
+    public float activeMa;
+    public Cluster[] clusters;
+
+    public static class Cluster {
+        public int coreCount;
+        public float onMa;
+        public Frequency[] frequencies;
+    }
+
+    public static class Frequency {
+        public int speedHz;
+        public float onMa;
+    }
+
+    public static class Builder {
+        private float mSuspendMa;
+        private float mIdleMa;
+        private float mActiveMa;
+        private int[] mCoreCount;
+        private HashMap<Integer,Float> mClusterOnPower = new HashMap<Integer,Float>();
+        private HashMap<Integer,int[]> mCoreSpeeds = new HashMap<Integer,int[]>();
+        private HashMap<Integer,float[]> mCorePower = new HashMap<Integer,float[]>();
+
+        public Builder() {
+        }
+
+        public void setSuspendMa(float value) throws ParseException {
+            mSuspendMa = value;
+        }
+
+        public void setIdleMa(float value) throws ParseException {
+            mIdleMa = value;
+        }
+
+        public void setActiveMa(float value) throws ParseException {
+            mActiveMa = value;
+        }
+
+        public void setCoreCount(int[] value) throws ParseException {
+            mCoreCount = Arrays.copyOf(value, value.length);
+        }
+
+        public void setClusterPower(int cluster, float value) throws ParseException {
+            mClusterOnPower.put(cluster, value);
+        }
+
+        public void setCoreSpeeds(int cluster, int[] value) throws ParseException {
+            mCoreSpeeds.put(cluster, Arrays.copyOf(value, value.length));
+            float[] power = mCorePower.get(cluster);
+            if (power != null && value.length != power.length) {
+                throw new ParseException("length of cpu.core_speeds.cluster" + cluster
+                        + " (" + value.length + ") is different from length of"
+                        + " cpu.core_power.cluster" + cluster + " (" + power.length + ")");
+            }
+            if (mCoreCount != null && cluster >= mCoreCount.length) {
+                throw new ParseException("cluster " + cluster
+                        + " in cpu.core_speeds.cluster" + cluster
+                        + " is larger than the number of clusters specified in cpu.clusters.cores ("
+                        + mCoreCount.length + ")");
+            }
+        }
+
+        public void setCorePower(int cluster, float[] value) throws ParseException {
+            mCorePower.put(cluster, Arrays.copyOf(value, value.length));
+            int[] speeds = mCoreSpeeds.get(cluster);
+            if (speeds != null && value.length != speeds.length) {
+                throw new ParseException("length of cpu.core_power.cluster" + cluster
+                        + " (" + value.length + ") is different from length of"
+                        + " cpu.clusters.cores" + cluster + " (" + speeds.length + ")");
+            }
+            if (mCoreCount != null && cluster >= mCoreCount.length) {
+                throw new ParseException("cluster " + cluster
+                        + " in cpu.core_power.cluster" + cluster
+                        + " is larger than the number of clusters specified in cpu.clusters.cores ("
+                        + mCoreCount.length + ")");
+            }
+        }
+
+        public CpuProfile build() throws ParseException {
+            final CpuProfile result = new CpuProfile();
+
+            // Validate cluster count
+
+            // All null or none null
+            // TODO
+
+            // Same size
+            // TODO
+
+            // No gaps
+            // TODO
+
+            // Fill in values
+            result.suspendMa = mSuspendMa;
+            result.idleMa = mIdleMa;
+            result.activeMa = mActiveMa;
+            if (mCoreCount != null) {
+                result.clusters = new Cluster[mCoreCount.length];
+                for (int i = 0; i < result.clusters.length; i++) {
+                    final Cluster cluster = result.clusters[i] = new Cluster();
+                    cluster.coreCount = mCoreCount[i];
+                    cluster.onMa = mClusterOnPower.get(i);
+                    int[] speeds = mCoreSpeeds.get(i);
+                    float[] power = mCorePower.get(i);
+                    cluster.frequencies = new Frequency[speeds.length];
+                    for (int j = 0; j < speeds.length; j++) {
+                        final Frequency freq = cluster.frequencies[j] = new Frequency();
+                        freq.speedHz = speeds[j];
+                        freq.onMa = power[j];
+                    }
+                }
+            }
+
+            return result;
+        }
+    }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
similarity index 70%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
index 27d25b8..c85f3ff 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/FlashlightProfile.java
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class FlashlightProfile extends ComponentProfile {
+    public float onMa;
 }
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
new file mode 100644
index 0000000..83c06a7
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/GpsProfile.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class GpsProfile extends ComponentProfile {
+    public float onMa;
+    public float[] signalQualityMa;
+
+    public static class Builder {
+        private float onMa;
+        private float[] mSignalQualityMa;
+
+        public Builder() {
+        }
+
+        public void setOnMa(float value) throws ParseException {
+            onMa = value;
+        }
+
+        public void setSignalMa(float[] value) throws ParseException {
+            mSignalQualityMa = value;
+        }
+
+        public GpsProfile build() throws ParseException {
+            GpsProfile result = new GpsProfile();
+            result.onMa = onMa;
+            result.signalQualityMa = mSignalQualityMa == null
+                    ? new float[0]
+                    : Arrays.copyOf(mSignalQualityMa, mSignalQualityMa.length);
+            return result;
+        }
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
new file mode 100644
index 0000000..cb70051
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the celluar modem on behalf of an app.
+ */
+public class ModemAppActivity extends ComponentActivity {
+    /**
+     * Construct a new ModemAppActivity.
+     */
+    public ModemAppActivity(AttributionKey attribution) {
+        super(attribution);
+    }
+
+    /**
+     * The number of packets received by the app.
+     */
+    public long rxPacketCount;
+
+    /**
+     * The number of packets sent by the app.
+     */
+    public long txPacketCount;
+
+    @Override
+    public ModemAppPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+        // Profile
+        final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+        if (modemProfile == null) {
+            // TODO: This is kind of a big problem...  Should this throw instead?
+            return null;
+        }
+
+        // Activity
+        final ModemGlobalActivity global
+                = (ModemGlobalActivity)activityReport.findGlobalComponent(Component.MODEM);
+        if (global == null) {
+            return null;
+        }
+
+        final double averageModemPowerMa = getAverageModemPowerMa(modemProfile);
+        final long totalPacketCount = global.rxPacketCount + global.txPacketCount;
+        final long appPacketCount = this.rxPacketCount + this.txPacketCount;
+
+        final ModemAppPower result = new ModemAppPower();
+        result.attribution = this.attribution;
+        result.activity = this;
+        result.powerMah = Conversion.msToHr(
+                (totalPacketCount > 0 ? (appPacketCount / (double)totalPacketCount) : 0)
+                * global.totalActiveTimeMs
+                * averageModemPowerMa);
+        return result;
+    }
+
+    static final double getAverageModemPowerMa(ModemProfile profile) {
+        double sumMa = profile.getRxMa();
+        for (float powerAtTxLevelMa: profile.getTxMa()) {
+            sumMa += powerAtTxLevelMa;
+        }
+        return sumMa / (profile.getTxMa().length + 1);
+    }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
similarity index 72%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
index 27d25b8..f553127 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemAppPower.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemAppPower extends ComponentPower<ModemAppActivity> {
 }
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
new file mode 100644
index 0000000..6dbfbc2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemBatteryStatsReader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import java.util.ArrayList;
+import java.util.List;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.RawBatteryStats;
+import com.android.powermodel.SpecialApp;
+
+public class ModemBatteryStatsReader {
+    private ModemBatteryStatsReader() {
+    }
+
+    public static List<ComponentActivity> createActivities(RawBatteryStats bs) {
+        final List<ComponentActivity> result = new ArrayList<ComponentActivity>();
+
+        // The whole system
+        createGlobal(result, bs);
+
+        // The apps
+        createApps(result, bs);
+
+        // The synthetic "cell" app.
+        createRemainder(result, bs);
+
+        return result;
+    }
+
+    private static void createGlobal(List<ComponentActivity> result, RawBatteryStats bs) {
+        final ModemGlobalActivity global
+                = new ModemGlobalActivity(new AttributionKey(SpecialApp.GLOBAL));
+
+        final RawBatteryStats.GlobalNetwork gn = bs.getSingle(RawBatteryStats.GlobalNetwork.class);
+        final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+        // Null here just means no network activity.
+        if (gn != null && misc != null) {
+            global.rxPacketCount = gn.mobileRxTotalPackets;
+            global.txPacketCount = gn.mobileTxTotalPackets;
+
+            global.totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+        }
+
+        result.add(global);
+    }
+
+    private static void createApps(List<ComponentActivity> result, RawBatteryStats bs) {
+        for (AttributionKey key: bs.getApps()) {
+            final int uid = key.getUid();
+            final RawBatteryStats.Network network
+                    = bs.getSingle(RawBatteryStats.Network.class, uid);
+
+            // Null here just means no network activity.
+            if (network != null) {
+                final ModemAppActivity app = new ModemAppActivity(key);
+
+                app.rxPacketCount = network.mobileRxPackets;
+                app.txPacketCount = network.mobileTxPackets;
+
+                result.add(app);
+            }
+        }
+    }
+
+    private static void createRemainder(List<ComponentActivity> result, RawBatteryStats bs) {
+        final RawBatteryStats.SignalStrengthTime strength
+                = bs.getSingle(RawBatteryStats.SignalStrengthTime.class);
+        final RawBatteryStats.SignalScanningTime scanning
+                = bs.getSingle(RawBatteryStats.SignalScanningTime.class);
+        final RawBatteryStats.Misc misc = bs.getSingle(RawBatteryStats.Misc.class);
+
+        if (strength != null && scanning != null && misc != null) {
+            final ModemRemainderActivity remainder
+                    = new ModemRemainderActivity(new AttributionKey(SpecialApp.REMAINDER));
+
+            // Signal strength buckets
+            remainder.strengthTimeMs = strength.phoneSignalStrengthTimeMs;
+
+            // Time spent scanning
+            remainder.scanningTimeMs = scanning.phoneSignalScanningTimeMs;
+
+            // Unaccounted for active time
+            final long totalActiveTimeMs = misc.mobileRadioActiveTimeMs;
+            long appActiveTimeMs = 0;
+            for (RawBatteryStats.Network nw: bs.getMultiple(RawBatteryStats.Network.class)) {
+                appActiveTimeMs += nw.mobileRadioActiveTimeUs / 1000;
+            }
+            remainder.activeTimeMs = totalActiveTimeMs - appActiveTimeMs;
+
+            result.add(remainder);
+        }
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
new file mode 100644
index 0000000..a53b53e
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemGlobalActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.ComponentPower;
+import com.android.powermodel.PowerProfile;
+
+/**
+ * Encapsulates total work done by the modem for the device.
+ */
+public class ModemGlobalActivity extends ComponentActivity {
+    /**
+     * Construct a new ModemGlobalActivity.
+     */
+    public ModemGlobalActivity(AttributionKey attribution) {
+        super(attribution);
+    }
+
+    /**
+     * Returns the total number of packets received in the whole device.
+     */
+    public long rxPacketCount;
+
+    /**
+     * Returns the total number of packets sent in the whole device.
+     */
+    public long txPacketCount;
+
+    /**
+     * Returns the total time the radio was active in the whole device.
+     */
+    public long totalActiveTimeMs;
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
new file mode 100644
index 0000000..cda72ee
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemProfile.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ModemProfile extends ComponentProfile {
+    public float sleepMa;
+    public float idleMa;
+    public float scanningMa;
+    public float rxMa;
+    public float[] txMa;
+
+    public float getSleepMa() {
+        return sleepMa;
+    }
+
+    public float getIdleMa() {
+        return idleMa;
+    }
+
+    public float getRxMa() {
+        return rxMa;
+    }
+
+    public float[] getTxMa() {
+        return Arrays.copyOf(txMa, txMa.length);
+    }
+
+    public float getScanningMa() {
+        return scanningMa;
+    }
+
+    public static class Builder {
+        private float mSleepMa;
+        private float mIdleMa;
+        private float mRxMa;
+        private float[] mTxMa;
+        private float mScanningMa;
+
+        public Builder() {
+        }
+
+        public void setSleepMa(float value) throws ParseException {
+            mSleepMa = value;
+        }
+
+        public void setIdleMa(float value) throws ParseException {
+            mIdleMa = value;
+        }
+
+        public void setRxMa(float value) throws ParseException {
+            mRxMa = value;
+        }
+
+        public void setTxMa(float[] value) throws ParseException {
+            mTxMa = Arrays.copyOf(value, value.length);
+        }
+
+        public void setScanningMa(float value) throws ParseException {
+            mScanningMa = value;
+        }
+
+        public ModemProfile build() throws ParseException {
+            ModemProfile result = new ModemProfile();
+            result.sleepMa = mSleepMa;
+            result.idleMa = mIdleMa;
+            result.rxMa = mRxMa;
+            result.txMa = mTxMa == null ? new float[0] : mTxMa;
+            result.scanningMa = mScanningMa;
+            return result;
+        }
+    }
+}
+
diff --git a/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
new file mode 100644
index 0000000..0e268c2
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 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.powermodel.component;
+
+import com.android.powermodel.ActivityReport;
+import com.android.powermodel.AttributionKey;
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentActivity;
+import com.android.powermodel.PowerProfile;
+import com.android.powermodel.util.Conversion;
+
+/**
+ * Encapsulates the work done by the remaining 
+ */
+public class ModemRemainderActivity extends ComponentActivity {
+    /**
+     * Construct a new ModemRemainderActivity.
+     */
+    public ModemRemainderActivity(AttributionKey attribution) {
+        super(attribution);
+    }
+
+    /**
+     * Number of milliseconds spent at each of the signal strengths.
+     */
+    public long[] strengthTimeMs;
+
+    /**
+     * Number of milliseconds spent scanning for a network.
+     */
+    public long scanningTimeMs;
+
+    /**
+     * Number of milliseconds that the radio is active for reasons other
+     * than an app transmitting and receiving data.
+     */
+    public long activeTimeMs;
+
+    @Override
+    public ModemRemainderPower applyProfile(ActivityReport activityReport, PowerProfile profile) {
+        // Profile
+        final ModemProfile modemProfile = (ModemProfile)profile.getComponent(Component.MODEM);
+        if (modemProfile == null) {
+            return null;
+        }
+
+        // Activity
+        final ModemRemainderPower result = new ModemRemainderPower();
+        result.attribution = this.attribution;
+        result.activity = this;
+
+        // strengthMah
+        // TODO: If the array lengths don't match... then?
+        result.strengthMah = new double[this.strengthTimeMs.length];
+        for (int i=0; i<this.strengthTimeMs.length; i++) {
+            result.strengthMah[i] = Conversion.msToHr(
+                    this.strengthTimeMs[i] * modemProfile.getTxMa()[i]);
+            result.powerMah += result.strengthMah[i];
+        }
+
+        // scanningMah
+        result.scanningMah = Conversion.msToHr(this.scanningTimeMs * modemProfile.getScanningMa());
+        result.powerMah += result.scanningMah;
+
+        // activeMah
+        result.activeMah = Conversion.msToHr(
+                this.activeTimeMs * ModemAppActivity.getAverageModemPowerMa(modemProfile));
+        result.powerMah += result.activeMah;
+
+        return result;
+    }
+}
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
similarity index 66%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
index 27d25b8..7f38cd3 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ModemRemainderPower.java
@@ -13,12 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import com.android.powermodel.Component;
+import com.android.powermodel.ComponentPower;
+
+public class ModemRemainderPower extends ComponentPower<ModemRemainderActivity> {
+
+    public double[] strengthMah;
+
+    public double scanningMah;
+
+    public double activeMah;
 }
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
index 27d25b8..e1051c6 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/ScreenProfile.java
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class ScreenProfile extends ComponentProfile {
+    public float onMa;
+    public float fullMa;
+    public float ambientMa;
 }
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
similarity index 71%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
index 27d25b8..5152795 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/VideoProfile.java
@@ -13,12 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class VideoProfile extends ComponentProfile {
+    public float onMa;
 }
+
+
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
similarity index 67%
copy from core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
copy to tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
index 27d25b8..6f424bf 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/tools/powermodel/src/com/android/powermodel/component/WifiProfile.java
@@ -13,12 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-/**
- * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
- * @hide
- */
-oneway interface IBiometricPromptReceiver {
-    void onDialogDismissed(int reason);
+package com.android.powermodel.component;
+
+import java.util.Arrays;
+
+import com.android.powermodel.ComponentProfile;
+import com.android.powermodel.ParseException;
+
+public class WifiProfile extends ComponentProfile {
+    public float idleMa;
+    public float rxMa;
+    public float txMa;
 }
+
diff --git a/tools/powermodel/src/com/android/powermodel/util/Conversion.java b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
new file mode 100644
index 0000000..e556c25
--- /dev/null
+++ b/tools/powermodel/src/com/android/powermodel/util/Conversion.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.powermodel.util;
+
+public class Conversion {
+
+    /**
+     * Convert the the float[] to an int[].
+     * <p>
+     * Values are rounded to the nearest integral value. Null input
+     * results in null output.
+     */
+    public static int[] toIntArray(float[] value) {
+        if (value == null) {
+            return null;
+        }
+        int[] result = new int[value.length];
+        for (int i=0; i<result.length; i++) {
+            result[i] = (int)(value[i] + 0.5f);
+        }
+        return result;
+    }
+    
+    public static double msToHr(double ms) {
+        return ms / 3600.0 / 1000.0;
+    }
+
+    /**
+     * No public constructor.
+     */
+    private Conversion() {
+    }
+}
diff --git a/tools/powermodel/test-resource/bs.csv b/tools/powermodel/test-resource/bs.csv
new file mode 100644
index 0000000..6e84120
--- /dev/null
+++ b/tools/powermodel/test-resource/bs.csv
@@ -0,0 +1,7 @@
+9,0,i,vers,32,177,PPR1.180326.002,PQ1A.181105.015
+9,0,i,uid,10139,com.google.android.gm
+9,0,l,gn,108060756,17293456,4896592,3290614,97840,72941,6903,8107,390,105
+9,0,l,m,2590630,0,384554,3943868,5113727,265,2565483,0,16,0,0,0,0,192,25331,3472068,17,3543323,14,614050,0
+9,10139,l,nt,13688501,534571,13842,7792,9925,5577,30,67,190051799,27,0,0,5,3,126020,42343,13842,7792,207,167,30,67
+9,0,l,sgt,3066958,0,34678,1643364,7045084
+9,0,l,sst,2443805
diff --git a/tools/powermodel/test-resource/power_profile.xml b/tools/powermodel/test-resource/power_profile.xml
new file mode 100644
index 0000000..8e388ea
--- /dev/null
+++ b/tools/powermodel/test-resource/power_profile.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2018 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.
+-->
+
+<!-- Test power profile that parses correctly. -->
+<device>
+    <item name="battery.capacity">2915</item>
+
+    <!-- Number of cores each CPU cluster contains -->
+    <array name="cpu.clusters.cores">
+        <value>4</value>
+        <value>2</value>
+    </array>
+
+    <!-- Power consumption when CPU is suspended -->
+    <item name="cpu.suspend">1.3</item>
+
+    <!-- Additional power consumption when CPU is in a kernel idle loop -->
+    <item name="cpu.idle">3.9</item>
+
+    <!-- Additional power consumption by CPU excluding cluster and core when
+         running -->
+    <item name="cpu.active">18.33</item>
+
+    <!-- Additional power consumption by CPU cluster0 itself when running
+         excluding cores in it -->
+    <item name="cpu.cluster_power.cluster0">2.41</item>
+
+    <!-- Additional power consumption by CPU cluster1 itself when running
+         excluding cores in it -->
+    <item name="cpu.cluster_power.cluster1">5.29</item>
+
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster0">
+        <value>100000</value>
+        <value>303200</value>
+        <value>380000</value>
+        <value>476000</value>
+        <value>552800</value>
+        <value>648800</value>
+        <value>725600</value>
+        <value>802400</value>
+        <value>879200</value>
+    </array>
+
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster1">
+        <value>825600</value>
+        <value>902400</value>
+        <value>979200</value>
+        <value>1056000</value>
+        <value>1209600</value>
+        <value>1286400</value>
+        <value>1363200</value>
+    </array>
+
+    <!-- Additional power used by a CPU core from cluster 0 when running at
+         different speeds, excluding cluster and active cost -->
+    <array name="cpu.core_power.cluster0">
+        <value>0.29</value>
+        <value>0.63</value>
+        <value>1.23</value>
+        <value>1.24</value>
+        <value>2.47</value>
+        <value>2.54</value>
+        <value>3.60</value>
+        <value>3.64</value>
+        <value>4.42</value>
+    </array>
+
+    <!-- Additional power used by a CPU core from cluster 1 when running at
+         different speeds, excluding cluster and active cost -->
+    <array name="cpu.core_power.cluster1">
+        <value>28.98</value>
+        <value>31.40</value>
+        <value>33.33</value>
+        <value>40.12</value>
+        <value>44.10</value>
+        <value>90.14</value>
+        <value>100</value>
+    </array>
+
+    <!-- Additional power used when screen is ambient mode -->
+    <item name="ambient.on">12</item>
+
+    <!-- Additional power used when screen is turned on at minimum brightness -->
+    <item name="screen.on">102.4</item>
+    <!-- Additional power used when screen is at maximum brightness, compared to
+         screen at minimum brightness -->
+    <item name="screen.full">1234</item>
+
+    <!-- Average power used by the camera flash module when on -->
+    <item name="camera.flashlight">1233.47</item>
+
+    <!-- Average power use by the camera subsystem for a typical camera
+         application. Intended as a rough estimate for an application running a
+         preview and capturing approximately 10 full-resolution pictures per
+         minute. -->
+    <item name="camera.avg">941</item>
+
+    <!-- Additional power used when video is playing -->
+    <item name="video">123</item>
+
+    <!-- Additional power used when audio is playing -->
+    <item name="audio">12</item>
+
+    <!-- Cellular modem related values.-->
+    <item name="modem.controller.sleep">1</item>
+    <item name="modem.controller.idle">44</item>
+    <item name="modem.controller.rx">11</item>
+    <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+      <value>16</value>
+      <value>19</value>
+      <value>22</value>
+      <value>73</value>
+      <value>132</value>
+    </array>
+    <item name="modem.controller.voltage">1400</item>
+    <item name="radio.scanning">12</item>
+
+    <!-- GPS related values.-->
+    <item name="gps.on">1</item>
+    <array name="gps.signalqualitybased"> <!-- Strength 0 to 1 -->
+      <value>88</value>
+      <value>07</value>
+    </array>
+    <item name="gps.voltage">1500</item>
+
+    <!-- Idle Receive current for wifi radio in mA.-->
+    <item name="wifi.controller.idle">2</item>
+
+    <!-- Rx current for wifi radio in mA.-->
+    <item name="wifi.controller.rx">123</item>
+
+    <!-- Tx current for wifi radio in mA-->
+    <item name="wifi.controller.tx">333</item>
+
+    <!-- Operating volatage for wifi radio in mV.-->
+    <item name="wifi.controller.voltage">3700</item>
+
+    <!-- Idle current for bluetooth in mA.-->
+    <item name="bluetooth.controller.idle">0.02</item>
+
+    <!-- Rx current for bluetooth in mA.-->
+    <item name="bluetooth.controller.rx">3</item>
+
+    <!-- Tx current for bluetooth in mA-->
+    <item name="bluetooth.controller.tx">5</item>
+
+    <!-- Operating voltage for bluetooth in mV.-->
+    <item name="bluetooth.controller.voltage">3300</item>
+
+</device>
+
+
diff --git a/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
new file mode 100644
index 0000000..e7b2c37
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/BatteryStatsReaderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppActivity;
+import com.android.powermodel.component.ModemGlobalActivity;
+import com.android.powermodel.component.ModemRemainderActivity;
+
+/**
+ * Tests {@link BatteryStatsReader}.
+ */
+public class BatteryStatsReaderTest {
+    private static InputStream loadCsvStream() {
+        return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+    }
+
+    @Test public void testModemGlobal() throws Exception {
+        final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+        final AppActivity global = report.findApp(SpecialApp.GLOBAL);
+        Assert.assertNotNull(global);
+
+        final ModemGlobalActivity modem
+                = (ModemGlobalActivity)global.getComponentActivity(Component.MODEM);
+        Assert.assertNotNull(modem);
+        Assert.assertEquals(97840, modem.rxPacketCount);
+        Assert.assertEquals(72941, modem.txPacketCount);
+        Assert.assertEquals(5113727, modem.totalActiveTimeMs);
+    }
+
+    @Test public void testModemApp() throws Exception {
+        final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+        final List<AppActivity> gmailList = report.findApp("com.google.android.gm");
+        Assert.assertEquals(1, gmailList.size());
+        final AppActivity gmail = gmailList.get(0);
+
+        final ModemAppActivity modem
+                = (ModemAppActivity)gmail.getComponentActivity(Component.MODEM);
+        Assert.assertNotNull(modem);
+        Assert.assertEquals(9925, modem.rxPacketCount);
+        Assert.assertEquals(5577, modem.txPacketCount);
+    }
+
+    @Test public void testModemRemainder() throws Exception {
+        final ActivityReport report = BatteryStatsReader.parse(loadCsvStream());
+
+        final AppActivity remainder = report.findApp(SpecialApp.REMAINDER);
+        Assert.assertNotNull(remainder);
+
+        final ModemRemainderActivity modem
+                = (ModemRemainderActivity)remainder.getComponentActivity(Component.MODEM);
+        Assert.assertNotNull(modem);
+        Assert.assertArrayEquals(new long[] { 3066958, 0, 34678, 1643364, 7045084 },
+                modem.strengthTimeMs);
+        Assert.assertEquals(2443805, modem.scanningTimeMs);
+        Assert.assertEquals(4923676, modem.activeTimeMs);
+    }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/CsvParserTest.java b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
new file mode 100644
index 0000000..55dde41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/CsvParserTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class CsvParserTest {
+
+    class LineCollector implements CsvParser.LineProcessor {
+        ArrayList<ArrayList<String>> results = new ArrayList<ArrayList<String>>();
+
+        @Override
+        public void onLine(int lineNumber, ArrayList<String> fields) {
+            System.out.println(lineNumber);
+            for (String str: fields) {
+                System.out.println("-->" + str + "<--");
+            }
+            results.add(fields);
+        }
+    }
+
+    private void assertEquals(String[][] expected, ArrayList<ArrayList<String>> results) {
+        final String[][] resultArray = new String[results.size()][];
+        for (int i=0; i<results.size(); i++) {
+            final ArrayList<String> list = results.get(i);
+            resultArray[i] = list.toArray(new String[list.size()]);
+        }
+        Assert.assertArrayEquals(expected, resultArray);
+    }
+
+    private String makeString(int length) {
+        final StringBuilder str = new StringBuilder();
+        for (int i=0; i<length; i++) {
+            str.append('a');
+        }
+        return str.toString();
+    }
+
+    @Test public void testEmpty() throws Exception {
+        final String text = "";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                }, collector.results);
+    }
+
+    @Test public void testOnlyNewline() throws Exception {
+        final String text = "\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                }, collector.results);
+    }
+
+    @Test public void testTwoLines() throws Exception {
+        final String text = "one,twoo,3\nfour,5,six\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "one", "twoo", "3", },
+                    { "four", "5", "six", },
+                }, collector.results);
+    }
+
+    
+    @Test public void testEscapedEmpty() throws Exception {
+        final String text = "\"\",\"\",\"\"\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "", "", "", },
+                }, collector.results);
+    }
+
+    @Test public void testEscapedText() throws Exception {
+        final String text = "\"one\",\"twoo\",\"3\"\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "one", "twoo", "3", },
+                }, collector.results);
+    }
+
+    @Test public void testEscapedQuotes() throws Exception {
+        final String text = "\"\"\"\",\"\"\"\"\"\",\"\"\"\"\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "\"", "\"\"", "\"", },
+                }, collector.results);
+    }
+
+    @Test public void testEscapedCommas() throws Exception {
+        final String text = "\",\",\",\",\",\"\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { ",", ",", ",", },
+                }, collector.results);
+    }
+
+    @Test public void testEscapedQuotesAndCommas() throws Exception {
+        final String text = "\"\"\",\",\"\"\",\",\"\"\",\"\n";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "\",", "\",", "\",", },
+                }, collector.results);
+    }
+
+    @Test public void testNoNewline() throws Exception {
+        final String text = "a,b,c";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "a", "b", "c", }
+                }, collector.results);
+    }
+
+    @Test public void testNoNewlineWithCommas() throws Exception {
+        final String text = "a,b,,";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "a", "b", "", "" }
+                }, collector.results);
+    }
+
+    @Test public void testNoNewlineWithQuote() throws Exception {
+        final String text = "a,b,\",\"";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "a", "b", "," }
+                }, collector.results);
+    }
+
+    @Test public void testNoCommas() throws Exception {
+        final String text = "aasdfadfadfad";
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { "aasdfadfadfad", }
+                }, collector.results);
+    }
+
+    @Test public void testMaxLength() throws Exception {
+        final String text = makeString(CsvParser.MAX_FIELD_SIZE);
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { text, }
+                }, collector.results);
+    }
+
+    @Test public void testMaxLengthTwice() throws Exception {
+        String big = makeString(CsvParser.MAX_FIELD_SIZE);
+        final String text = big + "," + big;
+        System.out.println("Test: [" + text + "]");
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { big, big, }
+                }, collector.results);
+    }
+
+    @Test public void testTooLong() throws Exception {
+        final String text = makeString(CsvParser.MAX_FIELD_SIZE+1);
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        try {
+            CsvParser.parse(is, collector);
+            throw new RuntimeException("Expected CsvParser.parse to throw ParseException");
+        } catch (ParseException ex) {
+            // good
+        }
+    }
+
+    @Test public void testBufferBoundary() throws Exception {
+        final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+        final String text = big + ",b,c,d,e,f,g";
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { big, "b", "c", "d", "e", "f", "g", }
+                }, collector.results);
+    }
+
+    @Test public void testBufferBoundaryEmpty() throws Exception {
+        final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+        final String text = big + ",,,,,,";
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { big, "", "", "", "", "", "", }
+                }, collector.results);
+    }
+
+    // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+    @Test public void testBufferBoundaryEscapingEven() throws Exception {
+        final String big = makeString(CsvParser.MAX_FIELD_SIZE-2);
+        final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { big, "\"\"\"\"\"", big }
+                }, collector.results);
+    }
+
+    // Checks that the escaping and sawQuote behavior is correct at the buffer boundary
+    @Test public void testBufferBoundaryEscapingOdd() throws Exception {
+        final String big = makeString(CsvParser.MAX_FIELD_SIZE-3);
+        final String text = big + ",\"\"\"\"\"\"\"\"\"\"\"\"," + big;
+        final InputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
+        LineCollector collector = new LineCollector();
+
+        CsvParser.parse(is, collector);
+
+        assertEquals(new String[][] {
+                    { big, "\"\"\"\"\"", big }
+                }, collector.results);
+    }
+
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
new file mode 100644
index 0000000..ab45831
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerProfileTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.InputStream;
+
+import com.android.powermodel.component.CpuProfile;
+import com.android.powermodel.component.AudioProfile;
+import com.android.powermodel.component.BluetoothProfile;
+import com.android.powermodel.component.CameraProfile;
+import com.android.powermodel.component.FlashlightProfile;
+import com.android.powermodel.component.GpsProfile;
+import com.android.powermodel.component.ModemProfile;
+import com.android.powermodel.component.ScreenProfile;
+import com.android.powermodel.component.VideoProfile;
+import com.android.powermodel.component.WifiProfile;
+import org.junit.Assert;
+import org.junit.Test;
+
+/*
+ * Additional tests needed:
+ *   - CPU clusters with mismatching counts of speeds and coefficients
+ *   - Extra fields
+ *   - Name listed twice
+ */
+
+/**
+ * Tests {@link PowerProfile}
+ */
+public class PowerProfileTest {
+    private static final float EPSILON = 0.00001f;
+
+    private static InputStream loadPowerProfileStream() {
+        return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+    }
+
+    @Test public void testReadGood() throws Exception {
+        final InputStream is = loadPowerProfileStream();
+
+        final PowerProfile profile = PowerProfile.parse(is);
+
+        // Audio
+        final AudioProfile audio = (AudioProfile)profile.getComponent(Component.AUDIO);
+        Assert.assertEquals(12.0f, audio.onMa, EPSILON);
+
+        // Bluetooth
+        final BluetoothProfile bluetooth
+                = (BluetoothProfile)profile.getComponent(Component.BLUETOOTH);
+        Assert.assertEquals(0.02f, bluetooth.idleMa, EPSILON);
+        Assert.assertEquals(3.0f, bluetooth.rxMa, EPSILON);
+        Assert.assertEquals(5.0f, bluetooth.txMa, EPSILON);
+
+        // Camera
+        final CameraProfile camera = (CameraProfile)profile.getComponent(Component.CAMERA);
+        Assert.assertEquals(941.0f, camera.onMa, EPSILON);
+
+        // CPU
+        final CpuProfile cpu = (CpuProfile)profile.getComponent(Component.CPU);
+        Assert.assertEquals(1.3f, cpu.suspendMa, EPSILON);
+        Assert.assertEquals(3.9f, cpu.idleMa, EPSILON);
+        Assert.assertEquals(18.33f, cpu.activeMa, EPSILON);
+        Assert.assertEquals(2, cpu.clusters.length);
+        // Cluster 0
+        Assert.assertEquals(4, cpu.clusters[0].coreCount);
+        Assert.assertEquals(2.41f, cpu.clusters[0].onMa, EPSILON);
+        Assert.assertEquals(9, cpu.clusters[0].frequencies.length, EPSILON);
+        Assert.assertEquals(100000, cpu.clusters[0].frequencies[0].speedHz);
+        Assert.assertEquals(0.29f, cpu.clusters[0].frequencies[0].onMa, EPSILON);
+        Assert.assertEquals(303200, cpu.clusters[0].frequencies[1].speedHz);
+        Assert.assertEquals(0.63f, cpu.clusters[0].frequencies[1].onMa, EPSILON);
+        Assert.assertEquals(380000, cpu.clusters[0].frequencies[2].speedHz);
+        Assert.assertEquals(1.23f, cpu.clusters[0].frequencies[2].onMa, EPSILON);
+        Assert.assertEquals(476000, cpu.clusters[0].frequencies[3].speedHz);
+        Assert.assertEquals(1.24f, cpu.clusters[0].frequencies[3].onMa, EPSILON);
+        Assert.assertEquals(552800, cpu.clusters[0].frequencies[4].speedHz);
+        Assert.assertEquals(2.47f, cpu.clusters[0].frequencies[4].onMa, EPSILON);
+        Assert.assertEquals(648800, cpu.clusters[0].frequencies[5].speedHz);
+        Assert.assertEquals(2.54f, cpu.clusters[0].frequencies[5].onMa, EPSILON);
+        Assert.assertEquals(725600, cpu.clusters[0].frequencies[6].speedHz);
+        Assert.assertEquals(3.60f, cpu.clusters[0].frequencies[6].onMa, EPSILON);
+        Assert.assertEquals(802400, cpu.clusters[0].frequencies[7].speedHz);
+        Assert.assertEquals(3.64f, cpu.clusters[0].frequencies[7].onMa, EPSILON);
+        Assert.assertEquals(879200, cpu.clusters[0].frequencies[8].speedHz);
+        Assert.assertEquals(4.42f, cpu.clusters[0].frequencies[8].onMa, EPSILON);
+        // Cluster 1
+        Assert.assertEquals(2, cpu.clusters[1].coreCount);
+        Assert.assertEquals(5.29f, cpu.clusters[1].onMa, EPSILON);
+        Assert.assertEquals(7, cpu.clusters[1].frequencies.length, EPSILON);
+        Assert.assertEquals(825600, cpu.clusters[1].frequencies[0].speedHz);
+        Assert.assertEquals(28.98f, cpu.clusters[1].frequencies[0].onMa, EPSILON);
+        Assert.assertEquals(902400, cpu.clusters[1].frequencies[1].speedHz);
+        Assert.assertEquals(31.40f, cpu.clusters[1].frequencies[1].onMa, EPSILON);
+        Assert.assertEquals(979200, cpu.clusters[1].frequencies[2].speedHz);
+        Assert.assertEquals(33.33f, cpu.clusters[1].frequencies[2].onMa, EPSILON);
+        Assert.assertEquals(1056000, cpu.clusters[1].frequencies[3].speedHz);
+        Assert.assertEquals(40.12f, cpu.clusters[1].frequencies[3].onMa, EPSILON);
+        Assert.assertEquals(1209600, cpu.clusters[1].frequencies[4].speedHz);
+        Assert.assertEquals(44.10f, cpu.clusters[1].frequencies[4].onMa, EPSILON);
+        Assert.assertEquals(1286400, cpu.clusters[1].frequencies[5].speedHz);
+        Assert.assertEquals(90.14f, cpu.clusters[1].frequencies[5].onMa, EPSILON);
+        Assert.assertEquals(1363200, cpu.clusters[1].frequencies[6].speedHz);
+        Assert.assertEquals(100f, cpu.clusters[1].frequencies[6].onMa, EPSILON);
+
+        // Flashlight
+        final FlashlightProfile flashlight
+                = (FlashlightProfile)profile.getComponent(Component.FLASHLIGHT);
+        Assert.assertEquals(1233.47f, flashlight.onMa, EPSILON);
+
+        // GPS
+        final GpsProfile gps = (GpsProfile)profile.getComponent(Component.GPS);
+        Assert.assertEquals(1.0f, gps.onMa, EPSILON);
+        Assert.assertEquals(2, gps.signalQualityMa.length);
+        Assert.assertEquals(88.0f, gps.signalQualityMa[0], EPSILON);
+        Assert.assertEquals(7.0f, gps.signalQualityMa[1], EPSILON);
+
+        // Modem
+        final ModemProfile modem = (ModemProfile)profile.getComponent(Component.MODEM);
+        Assert.assertEquals(1.0f, modem.sleepMa, EPSILON);
+        Assert.assertEquals(44.0f, modem.idleMa, EPSILON);
+        Assert.assertEquals(12.0f, modem.scanningMa, EPSILON);
+        Assert.assertEquals(11.0f, modem.rxMa, EPSILON);
+        Assert.assertEquals(5, modem.txMa.length);
+        Assert.assertEquals(16.0f, modem.txMa[0], EPSILON);
+        Assert.assertEquals(19.0f, modem.txMa[1], EPSILON);
+        Assert.assertEquals(22.0f, modem.txMa[2], EPSILON);
+        Assert.assertEquals(73.0f, modem.txMa[3], EPSILON);
+        Assert.assertEquals(132.0f, modem.txMa[4], EPSILON);
+
+        // Screen
+        final ScreenProfile screen = (ScreenProfile)profile.getComponent(Component.SCREEN);
+        Assert.assertEquals(102.4f, screen.onMa, EPSILON);
+        Assert.assertEquals(1234.0f, screen.fullMa, EPSILON);
+        Assert.assertEquals(12.0f, screen.ambientMa, EPSILON);
+
+        // Video
+        final VideoProfile video = (VideoProfile)profile.getComponent(Component.VIDEO);
+        Assert.assertEquals(123.0f, video.onMa, EPSILON);
+
+        // Wifi
+        final WifiProfile wifi = (WifiProfile)profile.getComponent(Component.WIFI);
+        Assert.assertEquals(2.0f, wifi.idleMa, EPSILON);
+        Assert.assertEquals(123.0f, wifi.rxMa, EPSILON);
+        Assert.assertEquals(333.0f, wifi.txMa, EPSILON);
+    }
+}
diff --git a/tools/powermodel/test/com/android/powermodel/PowerReportTest.java b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
new file mode 100644
index 0000000..1a61737
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/PowerReportTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+import com.android.powermodel.component.ModemAppPower;
+import com.android.powermodel.component.ModemRemainderPower;
+
+/**
+ * Tests {@link PowerReport}.
+ */
+public class PowerReportTest {
+    private static final double EPSILON = 0.001;
+    private static final double MS_PER_HR = 3600000.0;
+
+    private static final double AVERAGE_MODEM_POWER = ((11+16+19+22+73+132) / 6.0);
+    private static final double GMAIL_MODEM_MAH = ((9925+5577) / (double)(97840+72941))
+            * 5113727 * AVERAGE_MODEM_POWER * (1.0 / 3600 / 1000);
+    private static final double GMAIL_MAH
+            = GMAIL_MODEM_MAH;
+
+    private static final double REMAINDER_MODEM_MAH
+            =  (1.0 / 3600 / 1000)
+            * ((3066958 * 16) + (0 * 19) + (34678 * 22) + (1643364 * 73) + (7045084 * 132)
+                + (2443805 * 12)
+                + (4923676 * AVERAGE_MODEM_POWER));
+    private static final double REMAINDER_MAH
+            = REMAINDER_MODEM_MAH;
+
+    private static final double TOTAL_MAH
+            = GMAIL_MAH
+            + REMAINDER_MAH;
+
+    private static InputStream loadPowerProfileStream() {
+        return PowerProfileTest.class.getResourceAsStream("/power_profile.xml");
+    }
+
+    private static InputStream loadCsvStream() {
+        return BatteryStatsReaderTest.class.getResourceAsStream("/bs.csv");
+    }
+
+    private static PowerReport loadPowerReport() throws Exception {
+        final PowerProfile profile = PowerProfile.parse(loadPowerProfileStream());
+        final ActivityReport activity = BatteryStatsReader.parse(loadCsvStream());
+        return PowerReport.createReport(profile, activity);
+    }
+
+    @Test public void testModemApp() throws Exception {
+        final PowerReport report = loadPowerReport();
+
+        final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+        Assert.assertEquals(1, gmailList.size());
+        final AppPower gmail = gmailList.get(0);
+
+        final ModemAppPower modem = (ModemAppPower)gmail.getComponentPower(Component.MODEM);
+        Assert.assertNotNull(modem);
+        Assert.assertEquals(GMAIL_MODEM_MAH, modem.powerMah, EPSILON);
+    }
+
+    @Test public void testModemRemainder() throws Exception {
+        final PowerReport report = loadPowerReport();
+
+        final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+        Assert.assertNotNull(remainder);
+
+        final ModemRemainderPower modem
+                = (ModemRemainderPower)remainder.getComponentPower(Component.MODEM);
+        Assert.assertNotNull(modem);
+
+        Assert.assertArrayEquals(new double[] {
+                    3066958 * 16.0 / MS_PER_HR,
+                    0 * 19.0 / MS_PER_HR,
+                    34678 * 22.0 / MS_PER_HR,
+                    1643364 * 73.0 / MS_PER_HR,
+                    7045084 * 132.0 / MS_PER_HR },
+                modem.strengthMah, EPSILON);
+        Assert.assertEquals(2443805 * 12 / MS_PER_HR, modem.scanningMah, EPSILON);
+        Assert.assertEquals(4923676 * AVERAGE_MODEM_POWER / MS_PER_HR, modem.activeMah, EPSILON);
+
+        Assert.assertEquals(REMAINDER_MODEM_MAH, modem.powerMah, EPSILON);
+    }
+
+    @Test public void testAppTotal() throws Exception {
+        final PowerReport report = loadPowerReport();
+
+        final List<AppPower> gmailList = report.findApp("com.google.android.gm");
+        Assert.assertEquals(1, gmailList.size());
+        final AppPower gmail = gmailList.get(0);
+
+        Assert.assertEquals(GMAIL_MAH, gmail.getAppPowerMah(), EPSILON);
+    }
+
+    @Test public void testRemainderTotal() throws Exception {
+        final PowerReport report = loadPowerReport();
+
+        final AppPower remainder = report.findApp(SpecialApp.REMAINDER);
+        Assert.assertNotNull(remainder);
+
+        Assert.assertEquals(REMAINDER_MAH, remainder.getAppPowerMah(), EPSILON);
+    }
+
+    @Test public void testTotal() throws Exception {
+        final PowerReport report = loadPowerReport();
+
+        Assert.assertEquals(TOTAL_MAH, report.getTotalPowerMah(), EPSILON);
+    }
+}
+
diff --git a/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
new file mode 100644
index 0000000..fbcac41
--- /dev/null
+++ b/tools/powermodel/test/com/android/powermodel/RawBatteryStatsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.powermodel;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import org.junit.Test;
+import org.junit.Assert;
+
+/**
+ * Tests {@link RawBatteryStats}.
+ */
+public class RawBatteryStatsTest {
+    private static final int BS_VERSION = 32;
+
+    private static InputStream makeCsv(String... lines) {
+        return makeCsv(BS_VERSION, lines);
+    }
+
+    private static InputStream makeCsv(int version, String... lines) {
+        final StringBuilder result = new StringBuilder("9,0,i,vers,");
+        result.append(version);
+        result.append(",177,PPR1.180326.002,PQ1A.181105.015\n");
+        for (String line: lines) {
+            result.append(line);
+            result.append('\n');
+        }
+        return new ByteArrayInputStream(result.toString().getBytes(StandardCharsets.UTF_8));
+    }
+
+    @Test public void testVersion() throws Exception {
+        final InputStream is = makeCsv();
+
+        final RawBatteryStats bs = RawBatteryStats.parse(is);
+        final List<RawBatteryStats.Record> records = bs.getRecords();
+        final RawBatteryStats.Version line = (RawBatteryStats.Version)records.get(0);
+
+        Assert.assertEquals(0, bs.getWarnings().size());
+        Assert.assertEquals(true, line.complete);
+
+        Assert.assertEquals(9, line.lineVersion);
+        Assert.assertEquals(0, line.uid);
+        Assert.assertEquals(RawBatteryStats.Category.INFO, line.category);
+        Assert.assertEquals("vers", line.lineType);
+
+        Assert.assertEquals(BS_VERSION, line.dumpsysVersion);
+        Assert.assertEquals(177, line.parcelVersion);
+        Assert.assertEquals("PPR1.180326.002", line.startPlatformVersion);
+        Assert.assertEquals("PQ1A.181105.015", line.endPlatformVersion);
+    }
+
+    @Test public void testUid() throws Exception {
+        final InputStream is = makeCsv("9,0,i,uid,1000,com.example.app");
+
+        final RawBatteryStats bs = RawBatteryStats.parse(is);
+        final List<RawBatteryStats.Record> records = bs.getRecords();
+        final RawBatteryStats.Uid line = (RawBatteryStats.Uid)records.get(1);
+
+        Assert.assertEquals(1000, line.uidKey);
+        Assert.assertEquals("com.example.app", line.pkg);
+    }
+
+    @Test public void testVarargs() throws Exception {
+        final InputStream is = makeCsv("9,0,i,gmcd,1,2,3,4,5,6,7");
+
+        final RawBatteryStats bs = RawBatteryStats.parse(is);
+        final List<RawBatteryStats.Record> records = bs.getRecords();
+        final RawBatteryStats.GlobalModemController line
+                = (RawBatteryStats.GlobalModemController)records.get(1);
+
+        Assert.assertEquals(1, line.idleMs);
+        Assert.assertEquals(2, line.rxTimeMs);
+        Assert.assertEquals(3, line.powerMaMs);
+        Assert.assertEquals(4, line.txTimeMs.length);
+        Assert.assertEquals(4, line.txTimeMs[0]);
+        Assert.assertEquals(5, line.txTimeMs[1]);
+        Assert.assertEquals(6, line.txTimeMs[2]);
+        Assert.assertEquals(7, line.txTimeMs[3]);
+    }
+}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
index 1d4c435..d368136 100644
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -28,6 +28,7 @@
 
 import java.io.IOException;
 import java.io.PrintStream;
+import java.net.URLEncoder;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -38,7 +39,9 @@
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.lang.model.SourceVersion;
 import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 
 /**
@@ -108,10 +111,25 @@
                 "startline",
                 "startcol",
                 "endline",
-                "endcol"
+                "endcol",
+                "properties"
         );
     }
 
+    private String encodeAnnotationProperties(AnnotationMirror annotation) {
+        StringBuilder sb = new StringBuilder();
+        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
+                : annotation.getElementValues().entrySet()) {
+            if (sb.length() > 0) {
+                sb.append("&");
+            }
+            sb.append(e.getKey().getSimpleName())
+                    .append("=")
+                    .append(URLEncoder.encode(e.getValue().toString()));
+        }
+        return sb.toString();
+    }
+
     /**
      * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
      * attached to it. It returns CSV in the format:
@@ -137,7 +155,8 @@
                 lines.getLineNumber(pair.fst.pos().getStartPosition()),
                 lines.getColumnNumber(pair.fst.pos().getStartPosition()),
                 lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
-                lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)));
+                lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
+                encodeAnnotationProperties(unsupportedAppUsage));
     }
 
     /**
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 8585ae9..88b7e2e 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1128,7 +1128,10 @@
                 hadStringOrChain = true;
                 fprintf(out, "    jbyte* jbyte_array%d;\n", argIndex);
                 fprintf(out, "    const char* str%d;\n", argIndex);
-                fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
+                fprintf(out,
+                        "    if (arg%d != NULL && env->GetArrayLength(arg%d) > "
+                        "0) {\n",
+                        argIndex, argIndex);
                 fprintf(out,
                         "        jbyte_array%d = "
                         "env->GetByteArrayElements(arg%d, NULL);\n",
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 3ec8a41..c6acd02 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@
      */
     oneway void requestActivityInfo(in ResultReceiver result);
 
-    ParceledListSlice getConfiguredNetworks();
+    ParceledListSlice getConfiguredNetworks(String packageName);
 
     ParceledListSlice getPrivilegedConfiguredNetworks();
 
@@ -90,11 +90,11 @@
 
     List<ScanResult> getScanResults(String callingPackage);
 
-    void disconnect(String packageName);
+    boolean disconnect(String packageName);
 
-    void reconnect(String packageName);
+    boolean reconnect(String packageName);
 
-    void reassociate(String packageName);
+    boolean reassociate(String packageName);
 
     WifiInfo getConnectionInfo(String callingPackage);
 
@@ -190,8 +190,8 @@
 
     void unregisterNetworkRequestMatchCallback(int callbackIdentifier);
 
-    boolean addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+    int addNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
 
-    boolean removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
+    int removeNetworkSuggestions(in List<WifiNetworkSuggestion> networkSuggestions, in String packageName);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7aff03c..89b6703 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
@@ -137,6 +138,55 @@
     public static final int ERROR_AUTH_FAILURE_EAP_FAILURE = 3;
 
     /**
+     * Maximum number of active network suggestions allowed per app.
+     * @hide
+     */
+    public static final int NETWORK_SUGGESTIONS_MAX_PER_APP =
+            ActivityManager.isLowRamDeviceStatic() ? 256 : 1024;
+
+    /**
+     * Reason code if all of the network suggestions were successfully added or removed.
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_SUCCESS = 0;
+
+    /**
+     * Reason code if there was an internal error in the platform while processing the addition or
+     * removal of suggestions.
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL = 1;
+
+    /**
+     * Reason code if one or more of the network suggestions added already exists in platform's
+     * database.
+     * @see WifiNetworkSuggestion#equals(Object)
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE = 2;
+
+    /**
+     * Reason code if the number of network suggestions provided by the app crosses the max
+     * threshold set per app.
+     * @see #getMaxNumberOfNetworkSuggestionsPerApp()
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP = 3;
+
+    /**
+     * Reason code if one or more of the network suggestions removed does not exist in platform's
+     * database.
+     */
+    public static final int STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID = 4;
+
+    @IntDef(prefix = { "STATUS_NETWORK_SUGGESTIONS_" }, value = {
+            STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_INTERNAL,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_EXCEEDS_MAX_PER_APP,
+            STATUS_NETWORK_SUGGESTIONS_ERROR_REMOVE_INVALID,
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NetworkSuggestionsStatusCode {}
+
+    /**
      * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
      * @hide
      */
@@ -903,8 +953,12 @@
      * establish a connection to a remembered access point that is
      * within range, and will do periodic scans if there are remembered
      * access points but none are in range.
+     *
+     * @deprecated This API is non-functional and will have no impact.
      */
+    @Deprecated
     public static final int WIFI_MODE_FULL = 1;
+
     /**
      * In this Wi-Fi lock mode, Wi-Fi will be kept active,
      * but the only operation that will be supported is initiation of
@@ -913,28 +967,62 @@
      * nor will periodic scans be automatically performed looking for
      * remembered access points. Scans must be explicitly requested by
      * an application in this mode.
+     *
+     * @deprecated This API is non-functional and will have no impact.
      */
+    @Deprecated
     public static final int WIFI_MODE_SCAN_ONLY = 2;
+
     /**
-     * In this Wi-Fi lock mode, Wi-Fi will be kept active as in mode
-     * {@link #WIFI_MODE_FULL} but it operates at high performance
-     * with minimum packet loss and low packet latency even when
-     * the device screen is off. This mode will consume more power
-     * and hence should be used only when there is a need for such
-     * an active connection.
+     * In this Wi-Fi lock mode, Wi-Fi will not go to power save.
+     * This results in operating with low packet latency.
+     * The lock is active  even when the device screen is off or
+     * the acquiring application is running in the background.
+     * This mode will consume more power and hence should be used only
+     * when there is a need for this tradeoff.
      * <p>
      * An example use case is when a voice connection needs to be
-     * kept active even after the device screen goes off. Holding the
-     * regular {@link #WIFI_MODE_FULL} lock will keep the wifi
-     * connection active, but the connection can be lossy.
+     * kept active even after the device screen goes off.
      * Holding a {@link #WIFI_MODE_FULL_HIGH_PERF} lock for the
-     * duration of the voice call will improve the call quality.
+     * duration of the voice call may improve the call quality.
      * <p>
-     * When there is no support from the hardware, this lock mode
-     * will have the same behavior as {@link #WIFI_MODE_FULL}
+     * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_HIGH_PERF}
+     * lock will have no impact.
      */
     public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
 
+    /**
+     * In this Wi-Fi lock mode, Wi-Fi will operate with a priority to achieve low latency.
+     * {@link #WIFI_MODE_FULL_LOW_LATENCY} lock has the following limitations:
+     * <ol>
+     * <li>The lock is only active when the screen is on.</li>
+     * <li>The lock is only active when the acquiring app is running in the foreground.</li>
+     * </ol>
+     * Low latency mode optimizes for reduced packet latency,
+     * and as a result other performance measures may suffer when there are trade-offs to make:
+     * <ol>
+     * <li>Battery life may be reduced.</li>
+     * <li>Throughput may be reduced.</li>
+     * <li>Frequency of Wi-Fi scanning may be reduced. This may result in: </li>
+     * <ul>
+     * <li>The device may not roam or switch to the AP with highest signal quality.</li>
+     * <li>Location accuracy may be reduced.</li>
+     * </ul>
+     * </ol>
+     * <p>
+     * Example use cases are real time gaming or virtual reality applications where
+     * low latency is a key factor for user experience.
+     * <p>
+     * When there is no support from the hardware, the {@link #WIFI_MODE_FULL_LOW_LATENCY}
+     * lock will cause the device not to go power save.
+     * <p>
+     * Note: For an app which acquires both {@link #WIFI_MODE_FULL_LOW_LATENCY} and
+     * {@link #WIFI_MODE_FULL_HIGH_PERF} locks, {@link #WIFI_MODE_FULL_LOW_LATENCY}
+     * lock will be effective when app is running in foreground and screen is on,
+     * while the {@link #WIFI_MODE_FULL_HIGH_PERF} lock will take effect otherwise.
+     */
+    public static final int WIFI_MODE_FULL_LOW_LATENCY = 4;
+
     /** Anything worse than or equal to this will show 0 bars. */
     @UnsupportedAppUsage
     private static final int MIN_RSSI = -100;
@@ -1067,7 +1155,7 @@
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
-                mService.getConfiguredNetworks();
+                    mService.getConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1126,7 +1214,6 @@
      * @throws UnsupportedOperationException if Passpoint is not enabled on the device.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
         try {
@@ -1497,12 +1584,13 @@
      * suggestion back using this API.</li>
      *
      * @param networkSuggestions List of network suggestions provided by the app.
-     * @return true on success, false if any of the suggestions match (See
+     * @return Status code corresponding to the values in {@link NetworkSuggestionsStatusCode}.
      * {@link WifiNetworkSuggestion#equals(Object)} any previously provided suggestions by the app.
      * @throws {@link SecurityException} if the caller is missing required permissions.
      */
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
-    public boolean addNetworkSuggestions(@NonNull List<WifiNetworkSuggestion> networkSuggestions) {
+    public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
+            @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
             return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
@@ -1510,21 +1598,20 @@
         }
     }
 
-
     /**
-     * Remove a subset of or all of networks from previously provided suggestions by the app to the
-     * device.
+     * Remove some or all of the network suggestions that were previously provided by the app.
      * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters.
      * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used.
      *
      * @param networkSuggestions List of network suggestions to be removed. Pass an empty list
      *                           to remove all the previous suggestions provided by the app.
-     * @return true on success, false if any of the suggestions do not match any suggestions
-     * previously provided by the app. Any matching suggestions are removed from the device and
-     * will not be considered for any further connection attempts.
+     * @return Status code corresponding to the values in
+     * {@link NetworkSuggestionsStatusCode}.
+     * Any matching suggestions are removed from the device and will not be considered for any
+     * further connection attempts.
      */
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
-    public boolean removeNetworkSuggestions(
+    public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
             return mService.removeNetworkSuggestions(
@@ -1535,6 +1622,15 @@
     }
 
     /**
+     * Returns the max number of network suggestions that are allowed per app on the device.
+     * @see #addNetworkSuggestions(List)
+     * @see #removeNetworkSuggestions(List)
+     */
+    public int getMaxNumberOfNetworkSuggestionsPerApp() {
+        return NETWORK_SUGGESTIONS_MAX_PER_APP;
+    }
+
+    /**
      * Add or update a Passpoint configuration.  The configuration provides a credential
      * for connecting to Passpoint networks that are operated by the Passpoint
      * service provider specified in the configuration.
@@ -1761,8 +1857,7 @@
     @Deprecated
     public boolean disconnect() {
         try {
-            mService.disconnect(mContext.getOpPackageName());
-            return true;
+            return mService.disconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1786,8 +1881,7 @@
     @Deprecated
     public boolean reconnect() {
         try {
-            mService.reconnect(mContext.getOpPackageName());
-            return true;
+            return mService.reconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1811,8 +1905,7 @@
     @Deprecated
     public boolean reassociate() {
         try {
-            mService.reassociate(mContext.getOpPackageName());
-            return true;
+            return mService.reassociate(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1889,6 +1982,8 @@
     public static final int WIFI_FEATURE_WPA3_SUITE_B     = 0x10000000; // WPA3-Enterprise Suite-B
     /** @hide */
     public static final int WIFI_FEATURE_OWE              = 0x20000000; // Enhanced Open
+    /** @hide */
+    public static final int WIFI_FEATURE_LOW_LATENCY      = 0x40000000; // Low Latency modes
 
     private int getSupportedFeatures() {
         try {
@@ -2132,14 +2227,14 @@
      * existing networks. You should assume the network IDs can be different
      * after calling this method.
      *
-     * @return {@code false} Will always return true.
+     * @return {@code false}.
      * @deprecated There is no need to call this method -
      * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)}
      * and {@link #removeNetwork(int)} already persist the configurations automatically.
      */
     @Deprecated
     public boolean saveConfiguration() {
-        return true;
+        return false;
     }
 
     /**
@@ -3406,6 +3501,11 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void connect(WifiConfiguration config, ActionListener listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
         // Use INVALID_NETWORK_ID for arg1 when passing a config object
@@ -3426,7 +3526,12 @@
      * initialized again
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void connect(int networkId, ActionListener listener) {
         if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
         getChannel().sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
@@ -3452,7 +3557,12 @@
      * initialized again
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void save(WifiConfiguration config, ActionListener listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
         getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
@@ -3471,7 +3581,12 @@
      * initialized again
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void forget(int netId, ActionListener listener) {
         if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
         getChannel().sendMessage(FORGET_NETWORK, netId, putListener(listener));
@@ -3486,7 +3601,12 @@
      * initialized again
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void disable(int netId, ActionListener listener) {
         if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
         getChannel().sendMessage(DISABLE_NETWORK, netId, putListener(listener));
@@ -3498,6 +3618,12 @@
      * @param SSID, in the format of WifiConfiguration's SSID.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD,
+            android.Manifest.permission.NETWORK_STACK
+    })
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
@@ -3744,9 +3870,8 @@
     /**
      * Creates a new WifiLock.
      *
-     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
-     * {@link #WIFI_MODE_FULL_HIGH_PERF} and {@link #WIFI_MODE_SCAN_ONLY} for
-     * descriptions of the types of Wi-Fi locks.
+     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL_HIGH_PERF}
+     * and {@link #WIFI_MODE_FULL_LOW_LATENCY} for descriptions of the types of Wi-Fi locks.
      * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is
      *            never shown to the user under normal conditions, but should be descriptive
      *            enough to identify your application and the specific WifiLock within it, if it
@@ -3771,12 +3896,14 @@
      * @return a new, unacquired WifiLock with the given tag.
      *
      * @see WifiLock
+     *
+     * @deprecated This API is non-functional.
      */
+    @Deprecated
     public WifiLock createWifiLock(String tag) {
         return new WifiLock(WIFI_MODE_FULL, tag);
     }
 
-
     /**
      * Create a new MulticastLock
      *
diff --git a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
index 87706b9..f73b9e5 100644
--- a/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
+++ b/wifi/java/android/net/wifi/WifiNetworkConfigBuilder.java
@@ -42,8 +42,10 @@
 public class WifiNetworkConfigBuilder {
     private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
     private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
-    private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN =
+    private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
             new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+    private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
+            new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
     private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
             new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
     private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
@@ -189,7 +191,13 @@
      * Set the BSSID to use for filtering networks from scan results. Will only match network whose
      * BSSID is identical to the specified value.
      * <p>
-     * <li>Only allowed for creating network specifier, i.e {@link #buildNetworkSpecifier()}. </li>
+     * <li>For network requests ({@link NetworkSpecifier}), built using
+     * {@link #buildNetworkSpecifier}, sets the BSSID to use for filtering networks from scan
+     * results. Will only match networks whose BSSID is identical to specified value.</li>
+     * <li>For network suggestions ({@link WifiNetworkSuggestion}), built using
+     * {@link #buildNetworkSuggestion()}, sets a specific BSSID for the network suggestion.
+     * If set, only the specified BSSID with the specified SSID will be considered for connection.
+     * If not set, all BSSIDs with the specified SSID will be considered for connection.</li>
      * <li>Overrides any previous value set using {@link #setBssid(MacAddress)} or
      * {@link #setBssidPattern(MacAddress, MacAddress)}.</li>
      *
@@ -432,6 +440,9 @@
         if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) {
             wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\"";
         }
+        if (mBssidPatternMatcher.second == MATCH_EXACT_BSSID_PATTERN_MASK) {
+            wifiConfiguration.BSSID = mBssidPatternMatcher.first.toString();
+        }
         setSecurityParamsInWifiConfiguration(wifiConfiguration);
         wifiConfiguration.hiddenSSID = mIsHiddenSSID;
         wifiConfiguration.priority = mPriority;
@@ -460,7 +471,10 @@
                 && mSsidPatternMatcher.getPath().equals(MATCH_EMPTY_SSID_PATTERN_PATH)) {
             return true;
         }
-        if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN)) {
+        if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN1)) {
+            return true;
+        }
+        if (mBssidPatternMatcher.equals(MATCH_NO_BSSID_PATTERN2)) {
             return true;
         }
         return false;
@@ -474,6 +488,16 @@
         return false;
     }
 
+    private boolean hasSetMatchExactPattern() {
+        // exact ssid match with either match-all bssid or match-exact bssid.
+        if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL
+                && (mBssidPatternMatcher.equals(MATCH_ALL_BSSID_PATTERN)
+                || mBssidPatternMatcher.second.equals(MATCH_EXACT_BSSID_PATTERN_MASK))) {
+            return true;
+        }
+        return false;
+    }
+
     private void validateSecurityParams() {
         int numSecurityTypes = 0;
         numSecurityTypes += mIsEnhancedOpen ? 1 : 0;
@@ -566,9 +590,42 @@
     }
 
     /**
-     * Create a network suggestion object use in
-     * {@link WifiManager#addNetworkSuggestions(List)}.
+     * Create a network suggestion object use in {@link WifiManager#addNetworkSuggestions(List)}.
      * See {@link WifiNetworkSuggestion}.
+     *<p>
+     * Note: Apps can set a combination of SSID using {@link #setSsid(String)} and BSSID
+     * using {@link #setBssid(MacAddress)} to provide more fine grained network suggestions to the
+     * platform.
+     * </p>
+     *
+     * For example:
+     * To provide credentials for one open, one WPA2 and one WPA3 network with their
+     * corresponding SSID's:
+     * {@code
+     * final WifiNetworkSuggestion suggestion1 =
+     *      new WifiNetworkConfigBuilder()
+     *      .setSsid("test111111")
+     *      .buildNetworkSuggestion()
+     * final WifiNetworkSuggestion suggestion2 =
+     *      new WifiNetworkConfigBuilder()
+     *      .setSsid("test222222")
+     *      .setWpa2Passphrase("test123456")
+     *      .buildNetworkSuggestion()
+     * final WifiNetworkSuggestion suggestion3 =
+     *      new WifiNetworkConfigBuilder()
+     *      .setSsid("test333333")
+     *      .setWpa3Passphrase("test6789")
+     *      .buildNetworkSuggestion()
+     * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<WifiNetworkSuggestion> {{
+     *          add(suggestion1);
+     *          add(suggestion2);
+     *          add(suggestion3);
+     *      }};
+     * final WifiManager wifiManager =
+     *      context.getSystemService(Context.WIFI_SERVICE);
+     * wifiManager.addNetworkSuggestions(suggestionsList);
+     * ...
+     * }
      *
      * @return Instance of {@link WifiNetworkSuggestion}.
      * @throws IllegalStateException on invalid params set.
@@ -577,11 +634,14 @@
         if (mSsidPatternMatcher == null) {
             throw new IllegalStateException("setSsid should be invoked for suggestion");
         }
-        if (mSsidPatternMatcher.getType() != PatternMatcher.PATTERN_LITERAL
-                || mBssidPatternMatcher != null) {
-            throw new IllegalStateException("none of setSsidPattern/setBssidPattern/setBssid are"
+        setMatchAnyPatternIfUnset();
+        if (!hasSetMatchExactPattern()) {
+            throw new IllegalStateException("none of setSsidPattern/setBssidPattern are"
                     + " allowed for suggestion");
         }
+        if (hasSetMatchNonePattern()) {
+            throw new IllegalStateException("cannot set match-none for suggestion");
+        }
         validateSecurityParams();
 
         return new WifiNetworkSuggestion(
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 25121e2..760f1e6 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -104,8 +104,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.allowedKeyManagement,
-                suggestorUid);
+        return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
+                wifiConfiguration.allowedKeyManagement, suggestorUid);
     }
 
     /**
@@ -121,6 +121,7 @@
         }
         WifiNetworkSuggestion lhs = (WifiNetworkSuggestion) obj;
         return Objects.equals(this.wifiConfiguration.SSID, lhs.wifiConfiguration.SSID)
+                && Objects.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
                 && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
                                   lhs.wifiConfiguration.allowedKeyManagement)
                 && suggestorUid == lhs.suggestorUid;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 529548f..fc5caf0 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -184,6 +184,9 @@
     public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
     /** {@hide} */
     public static final String SCAN_PARAMS_WORK_SOURCE_KEY = "WorkSource";
+    /** {@hide} */
+    public static final String REQUEST_PACKAGE_NAME_KEY = "PackageName";
+
     /**
      * scan configuration parameters to be sent to {@link #startBackgroundScan}
      */
@@ -250,6 +253,14 @@
          */
         @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
         public int type = TYPE_LOW_LATENCY;
+        /**
+         * This scan request may ignore location settings while receiving scans. This should only
+         * be used in emergency situations.
+         * {@hide}
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
+        public boolean ignoreLocationSettings;
 
         /** Implement the Parcelable interface {@hide} */
         public int describeContents() {
@@ -267,6 +278,7 @@
             dest.writeInt(stepCount);
             dest.writeInt(isPnoScan ? 1 : 0);
             dest.writeInt(type);
+            dest.writeInt(ignoreLocationSettings ? 1 : 0);
             if (channels != null) {
                 dest.writeInt(channels.length);
                 for (int i = 0; i < channels.length; i++) {
@@ -301,6 +313,7 @@
                         settings.stepCount = in.readInt();
                         settings.isPnoScan = in.readInt() == 1;
                         settings.type = in.readInt();
+                        settings.ignoreLocationSettings = in.readInt() == 1;
                         int num_channels = in.readInt();
                         settings.channels = new ChannelSpec[num_channels];
                         for (int i = 0; i < num_channels; i++) {
@@ -798,6 +811,7 @@
         Bundle scanParams = new Bundle();
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
         mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
     }
 
@@ -812,8 +826,11 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+        Bundle scanParams = new Bundle();
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+        mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key, scanParams);
     }
+
     /**
      * reports currently available scan results on appropriate listeners
      * @return true if all scan results were reported correctly
@@ -821,7 +838,10 @@
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public boolean getScanResults() {
         validateChannel();
-        Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+        Bundle scanParams = new Bundle();
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+        Message reply =
+                mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0, 0, scanParams);
         return reply.what == CMD_OP_SUCCEEDED;
     }
 
@@ -856,6 +876,7 @@
         Bundle scanParams = new Bundle();
         scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
         scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
         mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
     }
 
@@ -870,7 +891,9 @@
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+        Bundle scanParams = new Bundle();
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+        mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key, scanParams);
     }
 
     /**
@@ -879,7 +902,10 @@
      */
     public List<ScanResult> getSingleScanResults() {
         validateChannel();
-        Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0);
+        Bundle scanParams = new Bundle();
+        scanParams.putString(REQUEST_PACKAGE_NAME_KEY, mContext.getOpPackageName());
+        Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SINGLE_SCAN_RESULTS, 0, 0,
+                scanParams);
         if (reply.what == WifiScanner.CMD_OP_SUCCEEDED) {
             return Arrays.asList(((ParcelableScanResults) reply.obj).getResults());
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 6772096..6631fa8 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -16,10 +16,16 @@
 
 package android.net.wifi.p2p;
 
+import android.annotation.IntDef;
 import android.annotation.UnsupportedAppUsage;
+import android.net.MacAddress;
 import android.net.wifi.WpsInfo;
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * A class representing a Wi-Fi P2p configuration for setting up a connection
@@ -38,12 +44,46 @@
      */
     public WpsInfo wps;
 
+    /**
+     * The network name of a group, should be configured by helper method
+     */
+    /** @hide */
+    public String networkName = "";
+
+    /**
+     * The passphrase of a group, should be configured by helper method
+     */
+    /** @hide */
+    public String passphrase = "";
+
+    /**
+     * The required band for Group Owner
+     */
+    /** @hide */
+    public int groupOwnerBand = GROUP_OWNER_BAND_AUTO;
+
     /** @hide */
     public static final int MAX_GROUP_OWNER_INTENT   =   15;
     /** @hide */
     @UnsupportedAppUsage
     public static final int MIN_GROUP_OWNER_INTENT   =   0;
 
+    /** @hide */
+    @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = {
+        GROUP_OWNER_BAND_AUTO,
+        GROUP_OWNER_BAND_2GHZ,
+        GROUP_OWNER_BAND_5GHZ
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GroupOwnerBandType {}
+
+    /**
+     * Recognized Group Owner required band.
+     */
+    public static final int GROUP_OWNER_BAND_AUTO = 0;
+    public static final int GROUP_OWNER_BAND_2GHZ = 1;
+    public static final int GROUP_OWNER_BAND_5GHZ = 2;
+
     /**
      * This is an integer value between 0 and 15 where 0 indicates the least
      * inclination to be a group owner and 15 indicates the highest inclination
@@ -115,6 +155,10 @@
         sbuf.append("\n wps: ").append(wps);
         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
         sbuf.append("\n persist: ").append(netId);
+        sbuf.append("\n networkName: ").append(networkName);
+        sbuf.append("\n passphrase: ").append(
+                TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>");
+        sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand);
         return sbuf.toString();
     }
 
@@ -130,6 +174,9 @@
             wps = new WpsInfo(source.wps);
             groupOwnerIntent = source.groupOwnerIntent;
             netId = source.netId;
+            networkName = source.networkName;
+            passphrase = source.passphrase;
+            groupOwnerBand = source.groupOwnerBand;
         }
     }
 
@@ -139,6 +186,9 @@
         dest.writeParcelable(wps, flags);
         dest.writeInt(groupOwnerIntent);
         dest.writeInt(netId);
+        dest.writeString(networkName);
+        dest.writeString(passphrase);
+        dest.writeInt(groupOwnerBand);
     }
 
     /** Implement the Parcelable interface */
@@ -150,6 +200,9 @@
                 config.wps = (WpsInfo) in.readParcelable(null);
                 config.groupOwnerIntent = in.readInt();
                 config.netId = in.readInt();
+                config.networkName = in.readString();
+                config.passphrase = in.readString();
+                config.groupOwnerBand = in.readInt();
                 return config;
             }
 
@@ -157,4 +210,140 @@
                 return new WifiP2pConfig[size];
             }
         };
+
+    /**
+     * Builder used to build {@link WifiP2pConfig} objects for
+     * creating or joining a group.
+     */
+    public static final class Builder {
+
+        private static final MacAddress MAC_ANY_ADDRESS =
+                MacAddress.fromString("00:00:00:00:00:00");
+
+        private MacAddress mDeviceAddress = MAC_ANY_ADDRESS;
+        private String mNetworkName = "";
+        private String mPassphrase = "";
+        private int mGroupOwnerBand = GROUP_OWNER_BAND_AUTO;
+        private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+
+        /**
+         * Specify the peer's MAC address. If not set, the device will
+         * try to find a peer whose SSID matches the network name as
+         * specified by {@link #setNetworkName(String)}. Specifying null will
+         * reset the peer's MAC address to "00:00:00:00:00:00".
+         * <p>
+         *     Optional. "00:00:00:00:00:00" by default.
+         *
+         * @param deviceAddress the peer's MAC address.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setDeviceAddress(MacAddress deviceAddress) {
+            if (deviceAddress == null) {
+                mDeviceAddress = MAC_ANY_ADDRESS;
+            } else {
+                mDeviceAddress = deviceAddress;
+            }
+            return this;
+        }
+
+        /**
+         * Specify the network name, a.k.a. group name,
+         * for creating or joining a group.
+         * <p>
+         *     Must be called - an empty network name is not valid.
+         *
+         * @param networkName network name of a group.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setNetworkName(String networkName) {
+            if (TextUtils.isEmpty(networkName)) {
+                throw new IllegalArgumentException(
+                        "network name must be non-empty.");
+            }
+            mNetworkName = networkName;
+            return this;
+        }
+
+        /**
+         * Specify the passphrase for creating or joining a group.
+         * <p>
+         *     Must be called - an empty passphrase is not valid.
+         *
+         * @param passphrase the passphrase of a group.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setPassphrase(String passphrase) {
+            if (TextUtils.isEmpty(passphrase)) {
+                throw new IllegalArgumentException(
+                        "passphrase must be non-empty.");
+            }
+            mPassphrase = passphrase;
+            return this;
+        }
+
+        /**
+         * Specify the band to use for creating the group. This method only applies when
+         * creating a group as Group Owner using {@link WifiP2pManager#createGroup}.
+         * The band should be {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ},
+         * or allow the system to pick the band by specifying {@link #GROUP_OWNER_BAND_AUTO}.
+         * If the Group Owner cannot create a group in the specified band, the operation will fail.
+         * <p>
+         *     Optional. {@link #GROUP_OWNER_BAND_AUTO} by default.
+         *
+         * @param band the required band of group owner.
+         *             This should be one of {@link #GROUP_OWNER_BAND_AUTO},
+         *             {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder setGroupOwnerBand(int band) {
+            mGroupOwnerBand = band;
+            return this;
+        }
+
+        /**
+         * Specify that the group configuration be persisted (i.e. saved).
+         * By default the group configuration will not be saved.
+         * <p>
+         *     Optional. false by default.
+         *
+         * @param persistent is this group persistent group.
+         * @return The builder to facilitate chaining
+         *         {@code builder.setXXX(..).setXXX(..)}.
+         */
+        public Builder enablePersistentMode(boolean persistent) {
+            if (persistent) {
+                mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+            } else {
+                mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+            }
+            return this;
+        }
+
+        /**
+         * Build {@link WifiP2pConfig} given the current requests made on the builder.
+         * @return {@link WifiP2pConfig} constructed based on builder method calls.
+         */
+        public WifiP2pConfig build() {
+            if (TextUtils.isEmpty(mNetworkName)) {
+                throw new IllegalStateException(
+                        "network name must be non-empty.");
+            }
+            if (TextUtils.isEmpty(mPassphrase)) {
+                throw new IllegalStateException(
+                        "passphrase must be non-empty.");
+            }
+
+            WifiP2pConfig config = new WifiP2pConfig();
+            config.deviceAddress = mDeviceAddress.toString();
+            config.networkName = mNetworkName;
+            config.passphrase = mPassphrase;
+            config.groupOwnerBand = mGroupOwnerBand;
+            config.netId = mNetId;
+            return config;
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index f58a006..b0ed110 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -16,6 +16,7 @@
 
 package android.net.wifi.p2p;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -25,6 +26,7 @@
 import android.annotation.SystemService;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.net.NetworkInfo;
 import android.net.wifi.WpsInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo;
 import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceResponse;
@@ -49,6 +51,8 @@
 
 import dalvik.system.CloseGuard;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -158,6 +162,14 @@
      */
     public static final String EXTRA_WIFI_STATE = "wifi_p2p_state";
 
+    /** @hide */
+    @IntDef({
+            WIFI_P2P_STATE_DISABLED,
+            WIFI_P2P_STATE_ENABLED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiP2pState {
+    }
+
     /**
      * Wi-Fi p2p is disabled.
      *
@@ -250,6 +262,14 @@
      */
     public static final String EXTRA_DISCOVERY_STATE = "discoveryState";
 
+    /** @hide */
+    @IntDef({
+            WIFI_P2P_DISCOVERY_STOPPED,
+            WIFI_P2P_DISCOVERY_STARTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WifiP2pDiscoveryState {
+    }
+
     /**
      * p2p discovery has stopped
      *
@@ -502,6 +522,21 @@
     /** @hide */
     public static final int SET_ONGOING_PEER_CONFIG_SUCCEEDED       = BASE + 89;
 
+    /** @hide */
+    public static final int REQUEST_P2P_STATE                       = BASE + 90;
+    /** @hide */
+    public static final int RESPONSE_P2P_STATE                      = BASE + 91;
+
+    /** @hide */
+    public static final int REQUEST_DISCOVERY_STATE                 = BASE + 92;
+    /** @hide */
+    public static final int RESPONSE_DISCOVERY_STATE                = BASE + 93;
+
+    /** @hide */
+    public static final int REQUEST_NETWORK_INFO                    = BASE + 94;
+    /** @hide */
+    public static final int RESPONSE_NETWORK_INFO                   = BASE + 95;
+
     /**
      * Create a new WifiP2pManager instance. Applications use
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -690,6 +725,43 @@
         public void onHandoverMessageAvailable(String handoverMessage);
     }
 
+    /** Interface for callback invocation when p2p state is available
+     *  in response to {@link #requestP2pState}.
+     */
+    public interface P2pStateListener {
+        /**
+         * The requested p2p state is available.
+         * @param state Wi-Fi p2p state
+         *        @see #WIFI_P2P_STATE_DISABLED
+         *        @see #WIFI_P2P_STATE_ENABLED
+         */
+        void onP2pStateAvailable(@WifiP2pState int state);
+    }
+
+    /** Interface for callback invocation when p2p state is available
+     *  in response to {@link #requestDiscoveryState}.
+     */
+    public interface DiscoveryStateListener {
+        /**
+         * The requested p2p discovery state is available.
+         * @param state Wi-Fi p2p discovery state
+         *        @see #WIFI_P2P_DISCOVERY_STARTED
+         *        @see #WIFI_P2P_DISCOVERY_STOPPED
+         */
+        void onDiscoveryStateAvailable(@WifiP2pDiscoveryState int state);
+    }
+
+    /** Interface for callback invocation when {@link android.net.NetworkInfo} is available
+     *  in response to {@link #requestNetworkInfo}.
+     */
+    public interface NetworkInfoListener {
+        /**
+         * The requested {@link android.net.NetworkInfo} is available
+         * @param networkInfo Wi-Fi p2p {@link android.net.NetworkInfo}
+         */
+        void onNetworkInfoAvailable(NetworkInfo networkInfo);
+    }
+
     /**
      * Interface for callback invocation when ongoing peer info is available
      * @hide
@@ -889,6 +961,24 @@
                                     .onOngoingPeerAvailable(peerConfig);
                         }
                         break;
+                    case RESPONSE_P2P_STATE:
+                        if (listener != null) {
+                            ((P2pStateListener) listener)
+                                    .onP2pStateAvailable(message.arg1);
+                        }
+                        break;
+                    case RESPONSE_DISCOVERY_STATE:
+                        if (listener != null) {
+                            ((DiscoveryStateListener) listener)
+                                    .onDiscoveryStateAvailable(message.arg1);
+                        }
+                        break;
+                    case RESPONSE_NETWORK_INFO:
+                        if (listener != null) {
+                            ((NetworkInfoListener) listener)
+                                    .onNetworkInfoAvailable((NetworkInfo) message.obj);
+                        }
+                        break;
                     default:
                         Log.d(TAG, "Ignored " + message);
                         break;
@@ -1126,6 +1216,38 @@
     }
 
     /**
+     * Create a p2p group with the current device as the group owner. This essentially creates
+     * an access point that can accept connections from legacy clients as well as other p2p
+     * devices.
+     *
+     * <p> An app should use {@link WifiP2pConfig.Builder} to build the configuration
+     * for a group.
+     *
+     * <p class="note"><strong>Note:</strong>
+     * This function would normally not be used unless the current device needs
+     * to form a p2p group as a Group Owner and allow peers to join it as either
+     * Group Clients or legacy Wi-Fi STAs.
+     *
+     * <p> The function call immediately returns after sending a group creation request
+     * to the framework. The application is notified of a success or failure to initiate
+     * group creation through listener callbacks {@link ActionListener#onSuccess} or
+     * {@link ActionListener#onFailure}.
+     *
+     * <p> Application can request for the group details with {@link #requestGroupInfo}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param config the configuration of a p2p group.
+     * @param listener for callbacks on success or failure. Can be null.
+     */
+    public void createGroup(@NonNull Channel c,
+            @Nullable WifiP2pConfig config,
+            @Nullable ActionListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(CREATE_GROUP, 0,
+                c.putListener(listener), config);
+    }
+
+    /**
      * Remove the current p2p group.
      *
      * <p> The function call immediately returns after sending a group removal request
@@ -1616,4 +1738,68 @@
         c.mAsyncChannel.sendMessage(SET_ONGOING_PEER_CONFIG, 0,
                 c.putListener(listener), config);
     }
+
+    /**
+     * Request p2p enabled state.
+     *
+     * <p> This state indicates whether Wi-Fi p2p is enabled or disabled.
+     * The valid value is one of {@link #WIFI_P2P_STATE_DISABLED} or
+     * {@link #WIFI_P2P_STATE_ENABLED}. The state is returned using the
+     * {@link P2pStateListener} listener.
+     *
+     * <p> This state is also included in the {@link #WIFI_P2P_STATE_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_WIFI_STATE}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when p2p state is available..
+     */
+    public void requestP2pState(@NonNull Channel c,
+            @NonNull P2pStateListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_P2P_STATE, 0, c.putListener(listener));
+    }
+
+    /**
+     * Request p2p discovery state.
+     *
+     * <p> This state indicates whether p2p discovery has started or stopped.
+     * The valid value is one of {@link #WIFI_P2P_DISCOVERY_STARTED} or
+     * {@link #WIFI_P2P_DISCOVERY_STOPPED}. The state is returned using the
+     * {@link DiscoveryStateListener} listener.
+     *
+     * <p> This state is also included in the {@link #WIFI_P2P_DISCOVERY_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_DISCOVERY_STATE}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when discovery state is available..
+     */
+    public void requestDiscoveryState(@NonNull Channel c,
+            @NonNull DiscoveryStateListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_DISCOVERY_STATE, 0, c.putListener(listener));
+    }
+
+    /**
+     * Request network info.
+     *
+     * <p> This method provides the network info in the form of a {@link android.net.NetworkInfo}.
+     * {@link android.net.NetworkInfo#isAvailable()} indicates the p2p availability and
+     * {@link android.net.NetworkInfo#getDetailedState()} reports the current fine-grained state
+     * of the network. This {@link android.net.NetworkInfo} is returned using the
+     * {@link NetworkInfoListener} listener.
+     *
+     * <p> This information is also included in the {@link #WIFI_P2P_CONNECTION_CHANGED_ACTION}
+     * broadcast event with extra {@link #EXTRA_NETWORK_INFO}.
+     *
+     * @param c is the channel created at {@link #initialize}.
+     * @param listener for callback when network info is available..
+     */
+    public void requestNetworkInfo(@NonNull Channel c,
+            @NonNull NetworkInfoListener listener) {
+        checkChannel(c);
+        if (listener == null) throw new IllegalArgumentException("This listener cannot be null.");
+        c.mAsyncChannel.sendMessage(REQUEST_NETWORK_INFO, 0, c.putListener(listener));
+    }
 }
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index 04bc557..0f4e3a8 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -73,7 +73,7 @@
     }
 
     @Override
-    public ParceledListSlice getConfiguredNetworks() {
+    public ParceledListSlice getConfiguredNetworks(String packageName) {
         throw new UnsupportedOperationException();
     }
 
@@ -188,17 +188,17 @@
     }
 
     @Override
-    public void disconnect(String packageName) {
+    public boolean disconnect(String packageName) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void reconnect(String packageName) {
+    public boolean reconnect(String packageName) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void reassociate(String packageName) {
+    public boolean reassociate(String packageName) {
         throw new UnsupportedOperationException();
     }
 
@@ -442,13 +442,13 @@
     }
 
     @Override
-    public boolean addNetworkSuggestions(
+    public int addNetworkSuggestions(
             List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public boolean removeNetworkSuggestions(
+    public int removeNetworkSuggestions(
             List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8fe5af9..13c8c9e 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1298,13 +1298,26 @@
      */
     @Test
     public void addRemoveNetworkSuggestions() throws Exception {
-        when(mWifiService.addNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
-        when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(true);
+        when(mWifiService.addNetworkSuggestions(any(List.class), anyString()))
+                .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+        when(mWifiService.removeNetworkSuggestions(any(List.class), anyString()))
+                .thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
 
-        assertTrue(mWifiManager.addNetworkSuggestions(new ArrayList<>()));
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+                mWifiManager.addNetworkSuggestions(new ArrayList<>()));
         verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
 
-        assertTrue(mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
+        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+                mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
         verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
     }
+
+    /**
+     * Verify call to {@link WifiManager#getMaxNumberOfNetworkSuggestionsPerApp()}.
+     */
+    @Test
+    public void getMaxNumberOfNetworkSuggestionsPerApp() {
+        assertEquals(WifiManager.NETWORK_SUGGESTIONS_MAX_PER_APP,
+                mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp());
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
index c455c6f..2505499 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkConfigBuilderTest.java
@@ -244,7 +244,7 @@
      * when match-none SSID pattern is set.
      */
     @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern() {
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern1() {
         new WifiNetworkConfigBuilder()
                 .setSsidPattern(new PatternMatcher("", PatternMatcher.PATTERN_LITERAL))
                 .buildNetworkSpecifier();
@@ -252,10 +252,21 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none SSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneSsidPattern2() {
+        new WifiNetworkConfigBuilder()
+                .setSsid("")
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
      * when match-none BSSID pattern is set.
      */
     @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern() {
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern1() {
         new WifiNetworkConfigBuilder()
                 .setBssidPattern(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS)
                 .buildNetworkSpecifier();
@@ -263,6 +274,28 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern2() {
+        new WifiNetworkConfigBuilder()
+                .setBssid(MacAddress.BROADCAST_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
+     * when match-none BSSID pattern is set.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
+        new WifiNetworkConfigBuilder()
+                .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+                .buildNetworkSpecifier();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSpecifier()} throws an exception
      * when SSID pattern is set for hidden network.
      */
     @Test(expected = IllegalStateException.class)
@@ -429,13 +462,15 @@
      * {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} for OWE network.
      */
     @Test
-    public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetwork() {
+    public void testWifiNetworkSuggestionBuilderForEnhancedOpenNetworkWithBssid() {
         WifiNetworkSuggestion suggestion = new WifiNetworkConfigBuilder()
                 .setSsid(TEST_SSID)
+                .setBssid(MacAddress.fromString(TEST_BSSID))
                 .setIsEnhancedOpen()
                 .buildNetworkSuggestion();
 
         assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
+        assertEquals(TEST_BSSID, suggestion.wifiConfiguration.BSSID);
         assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
                 .get(WifiConfiguration.KeyMgmt.OWE));
         assertNull(suggestion.wifiConfiguration.preSharedKey);
@@ -505,7 +540,7 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
-     * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is set.
+     * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
      */
     @Test(expected = IllegalStateException.class)
     public void testWifiNetworkSuggestionBuilderWithBssidPattern() {
@@ -518,18 +553,6 @@
 
     /**
      * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
-     * when {@link WifiNetworkConfigBuilder#setBssidPattern(MacAddress, MacAddress)} is set.
-     */
-    @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkSuggestionBuilderWithBssid() {
-        new WifiNetworkConfigBuilder()
-                .setSsid(TEST_SSID)
-                .setBssid(MacAddress.fromString(TEST_BSSID))
-                .buildNetworkSuggestion();
-    }
-
-    /**
-     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
      * when {@link WifiNetworkConfigBuilder#setSsid(String)} is not set.
      */
     @Test(expected = IllegalStateException.class)
@@ -539,6 +562,41 @@
     }
 
     /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setSsid(String)} is invoked with an invalid value.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithInvalidSsid() {
+        new WifiNetworkConfigBuilder()
+                .setSsid("")
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithInvalidBroadcastBssid() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.BROADCAST_ADDRESS)
+                .buildNetworkSuggestion();
+    }
+
+    /**
+     * Ensure {@link WifiNetworkConfigBuilder#buildNetworkSuggestion()} throws an exception
+     * when {@link WifiNetworkConfigBuilder#setBssid(MacAddress)} is invoked with an invalid value.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
+        new WifiNetworkConfigBuilder()
+                .setSsid(TEST_SSID)
+                .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+                .buildNetworkSuggestion();
+    }
+
+    /**
      * Ensure {@link WifiNetworkConfigBuilder#setPriority(int)} throws an exception
      * when the value is negative.
      */
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6bab60d..5cc8217 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -29,6 +29,7 @@
 @SmallTest
 public class WifiNetworkSuggestionTest {
     private static final String TEST_SSID = "\"Test123\"";
+    private static final String TEST_BSSID = "12:12:12:12:12:12";
     private static final String TEST_SSID_1 = "\"Test1234\"";
 
     /**
@@ -38,6 +39,7 @@
     public void testWifiNetworkSuggestionParcel() {
         WifiConfiguration configuration = new WifiConfiguration();
         configuration.SSID = TEST_SSID;
+        configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
                 new WifiNetworkSuggestion(configuration, false, true, 0);
@@ -65,18 +67,20 @@
 
     /**
      * Check NetworkSuggestion equals returns {@code true} for 2 network suggestions with the same
-     * SSID, key mgmt and UID.
+     * SSID, BSSID, key mgmt and UID.
      */
     @Test
     public void testWifiNetworkSuggestionEqualsSame() {
         WifiConfiguration configuration = new WifiConfiguration();
         configuration.SSID = TEST_SSID;
+        configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion =
                 new WifiNetworkSuggestion(configuration, true, false, 0);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
+        configuration1.BSSID = TEST_BSSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
                 new WifiNetworkSuggestion(configuration1, false, true, 0);
@@ -86,7 +90,7 @@
 
     /**
      * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
-     * key mgmt and UID, but different SSID.
+     * BSSID, key mgmt and UID, but different SSID.
      */
     @Test
     public void testWifiNetworkSuggestionEqualsFailsWhenSsidIsDifferent() {
@@ -107,7 +111,29 @@
 
     /**
      * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
-     * SSID and UID, but different key mgmt.
+     * SSID, key mgmt and UID, but different BSSID.
+     */
+    @Test
+    public void testWifiNetworkSuggestionEqualsFailsWhenBssidIsDifferent() {
+        WifiConfiguration configuration = new WifiConfiguration();
+        configuration.SSID = TEST_SSID;
+        configuration.BSSID = TEST_BSSID;
+        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion =
+                new WifiNetworkSuggestion(configuration, false, false, 0);
+
+        WifiConfiguration configuration1 = new WifiConfiguration();
+        configuration1.SSID = TEST_SSID;
+        configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+        WifiNetworkSuggestion suggestion1 =
+                new WifiNetworkSuggestion(configuration1, false, false, 0);
+
+        assertNotEquals(suggestion, suggestion1);
+    }
+
+    /**
+     * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
+     * SSID, BSSID and UID, but different key mgmt.
      */
     @Test
     public void testWifiNetworkSuggestionEqualsFailsWhenKeyMgmtIsDifferent() {
@@ -128,7 +154,7 @@
 
     /**
      * Check NetworkSuggestion equals returns {@code false} for 2 network suggestions with the same
-     * SSID and key mgmt, but different UID.
+     * SSID, BSSID and key mgmt, but different UID.
      */
     @Test
     public void testWifiNetworkSuggestionEqualsFailsWhenUidIsDifferent() {