Merge "Add VDM APIs to support permission streaming" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a271d06..08a09e1 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -15,6 +15,7 @@
 aconfig_srcjars = [
     ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
     ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
+    ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
     ":android.nfc.flags-aconfig-java{.generated_srcjars}",
     ":android.os.flags-aconfig-java{.generated_srcjars}",
     ":android.os.vibrator.flags-aconfig-java{.generated_srcjars}",
@@ -36,6 +37,7 @@
     ":hwui_flags_java_lib{.generated_srcjars}",
     ":display_flags_lib{.generated_srcjars}",
     ":android.multiuser.flags-aconfig-java{.generated_srcjars}",
+    ":android.app.flags-aconfig-java{.generated_srcjars}",
 ]
 
 filegroup {
@@ -327,3 +329,29 @@
     aconfig_declarations: "android.multiuser.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Activity Manager
+aconfig_declarations {
+    name: "android.app.flags-aconfig",
+    package: "android.app",
+    srcs: ["core/java/android/app/activity_manager.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.flags-aconfig-java",
+    aconfig_declarations: "android.app.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Broadcast Radio
+aconfig_declarations {
+    name: "android.hardware.radio.flags-aconfig",
+    package: "android.hardware.radio",
+    srcs: ["core/java/android/hardware/radio/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.hardware.radio.flags-aconfig-java",
+    aconfig_declarations: "android.hardware.radio.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 56a69b4..e5e0ad3 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -86,6 +86,9 @@
 
 droidstubs {
     name: "system-api-stubs-docs-non-updatable",
+    srcs: [
+        ":framework-minus-apex-aconfig-srcjars",
+    ],
     defaults: [
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
@@ -126,6 +129,9 @@
 
 droidstubs {
     name: "test-api-stubs-docs-non-updatable",
+    srcs: [
+        ":framework-minus-apex-aconfig-srcjars",
+    ],
     defaults: [
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
@@ -173,6 +179,9 @@
 
 droidstubs {
     name: "module-lib-api-stubs-docs-non-updatable",
+    srcs: [
+        ":framework-minus-apex-aconfig-srcjars",
+    ],
     defaults: [
         "android-non-updatable-stubs-defaults",
         "module-classpath-stubs-defaults",
diff --git a/core/api/current.txt b/core/api/current.txt
index d5b18ef..955858b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4616,9 +4616,9 @@
 
   public class ActivityManager {
     method public int addAppTask(@NonNull android.app.Activity, @NonNull android.content.Intent, @Nullable android.app.ActivityManager.TaskDescription, @NonNull android.graphics.Bitmap);
-    method public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
+    method @FlaggedApi("android.app.app_start_info") public void addStartInfoTimestamp(@IntRange(from=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START, to=android.app.ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int, long);
     method public void appNotResponding(@NonNull String);
-    method public void clearApplicationStartInfoCompletionListener();
+    method @FlaggedApi("android.app.app_start_info") public void clearApplicationStartInfoCompletionListener();
     method public boolean clearApplicationUserData();
     method public void clearWatchHeapLimit();
     method @RequiresPermission(android.Manifest.permission.DUMP) public void dumpPackageState(java.io.FileDescriptor, String);
@@ -4626,7 +4626,7 @@
     method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
     method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
     method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
-    method @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
+    method @FlaggedApi("android.app.app_start_info") @NonNull public java.util.List<android.app.ApplicationStartInfo> getHistoricalProcessStartReasons(@IntRange(from=0) int);
     method public int getLargeMemoryClass();
     method public int getLauncherLargeIconDensity();
     method public int getLauncherLargeIconSize();
@@ -4653,7 +4653,7 @@
     method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
     method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
     method @Deprecated public void restartPackage(String);
-    method public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
+    method @FlaggedApi("android.app.app_start_info") public void setApplicationStartInfoCompletionListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.ApplicationStartInfo>);
     method public void setProcessStateSummary(@Nullable byte[]);
     method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
@@ -5234,7 +5234,7 @@
     field public static final int REASON_USER_STOPPED = 11; // 0xb
   }
 
-  public final class ApplicationStartInfo implements android.os.Parcelable {
+  @FlaggedApi("android.app.app_start_info") public final class ApplicationStartInfo implements android.os.Parcelable {
     method public int describeContents();
     method public int getDefiningUid();
     method @Nullable public android.content.Intent getIntent();
@@ -39009,6 +39009,7 @@
     method @Nullable public java.util.Date getKeyValidityStart();
     method @NonNull public String getKeystoreAlias();
     method public int getMaxUsageCount();
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
     method public int getUserAuthenticationType();
@@ -39016,6 +39017,7 @@
     method public boolean isDevicePropertiesAttestationIncluded();
     method @NonNull public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isUnlockedDeviceRequired();
@@ -39047,6 +39049,7 @@
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMgf1Digests(@Nullable java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -39151,12 +39154,14 @@
     method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
     method @Nullable public java.util.Date getKeyValidityStart();
     method public int getMaxUsageCount();
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public java.util.Set<java.lang.String> getMgf1Digests();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
     method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public boolean isMgf1DigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
@@ -39178,6 +39183,7 @@
     method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
     method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
+    method @FlaggedApi("MGF1_DIGEST_SETTER") @NonNull public android.security.keystore.KeyProtection.Builder setMgf1Digests(@Nullable java.lang.String...);
     method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9b8d2b4..052d614 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -556,24 +556,24 @@
 
 package android.se.omapi {
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeFrameworkInitializer {
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeFrameworkInitializer {
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.se.omapi.SeServiceManager getSeServiceManager();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public static void setSeServiceManager(@NonNull android.se.omapi.SeServiceManager);
   }
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public class SeServiceManager {
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public class SeServiceManager {
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.se.omapi.SeServiceManager.ServiceRegisterer getSeManagerServiceRegisterer();
   }
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
-    ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public SeServiceManager.ServiceNotFoundException(@NonNull String);
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public static class SeServiceManager.ServiceNotFoundException extends java.lang.Exception {
+    ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public SeServiceManager.ServiceNotFoundException(@NonNull String);
   }
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public static final class SeServiceManager.ServiceRegisterer {
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder get();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void register(@NonNull android.os.IBinder);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public android.os.IBinder tryGet();
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public static final class SeServiceManager.ServiceRegisterer {
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder get();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.os.IBinder getOrThrow() throws android.se.omapi.SeServiceManager.ServiceNotFoundException;
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void register(@NonNull android.os.IBinder);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public android.os.IBinder tryGet();
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c3c3882..1d88e00 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -540,7 +540,7 @@
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
     method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
     method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser();
-    method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
+    method @FlaggedApi("android.app.app_start_info") @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List<android.app.ApplicationStartInfo> getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
     method @NonNull public java.util.Collection<java.util.Locale> getSupportedLocales();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
@@ -3193,7 +3193,7 @@
 
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void addActivityPolicyExemption(@NonNull android.content.ComponentName);
     method public void addSoundEffectListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
     method @NonNull public android.content.Context createContext();
@@ -3214,9 +3214,9 @@
     method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
-    method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void removeActivityPolicyExemption(@NonNull android.content.ComponentName);
     method public void removeSoundEffectListener(@NonNull android.companion.virtual.VirtualDeviceManager.SoundEffectListener);
-    method @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
+    method @FlaggedApi("android.companion.virtual.flags.dynamic_policy") @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setDevicePolicy(int, int);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
   }
@@ -3232,7 +3232,7 @@
     method public int getDefaultActivityPolicy();
     method public int getDefaultNavigationPolicy();
     method public int getDevicePolicy(int);
-    method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @Nullable public android.content.ComponentName getHomeComponent();
+    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @Nullable public android.content.ComponentName getHomeComponent();
     method public int getLockState();
     method @Nullable public String getName();
     method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
@@ -3247,7 +3247,7 @@
     field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
     field public static final int NAVIGATION_POLICY_DEFAULT_ALLOWED = 0; // 0x0
     field public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
-    field @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY) public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
+    field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
     field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
     field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
     field public static final int POLICY_TYPE_SENSORS = 0; // 0x0
@@ -3264,7 +3264,7 @@
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedCrossTaskNavigations(@NonNull java.util.Set<android.content.ComponentName>);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setDevicePolicy(int, int);
-    method @FlaggedApi(Flags.FLAG_VDM_CUSTOM_HOME) @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
+    method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setHomeComponent(@Nullable android.content.ComponentName);
     method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
@@ -3805,8 +3805,8 @@
   public class PackageInstaller {
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
-    method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi(Flags.FLAG_ARCHIVING) @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
     field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
     field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
@@ -3817,8 +3817,8 @@
     field public static final String EXTRA_DATA_LOADER_TYPE = "android.content.pm.extra.DATA_LOADER_TYPE";
     field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
     field public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
-    field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
-    field @FlaggedApi(Flags.FLAG_ARCHIVING) public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS";
+    field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME";
     field public static final int LOCATION_DATA_APP = 0; // 0x0
     field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
     field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
@@ -3879,7 +3879,7 @@
     method public static void forceSafeLabels();
     method @Deprecated @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager);
     method @NonNull public CharSequence loadSafeLabel(@NonNull android.content.pm.PackageManager, @FloatRange(from=0) float, int);
-    field @FlaggedApi(Flags.FLAG_ARCHIVING) public boolean isArchived;
+    field @FlaggedApi("android.content.pm.archiving") public boolean isArchived;
   }
 
   public abstract class PackageManager {
@@ -3929,7 +3929,7 @@
     method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
     method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
-    method @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+    method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
     method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
     method public void setSystemAppState(@NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
@@ -3980,7 +3980,7 @@
     field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
     field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 256; // 0x100
     field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
-    field @FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED) public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
+    field @FlaggedApi("android.content.pm.quarantined_enabled") public static final int FLAG_SUSPEND_QUARANTINED = 1; // 0x1
     field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff
     field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3
     field public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; // 0xffffffee
@@ -9632,67 +9632,67 @@
 
 package android.nfc.cardemulation {
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class AidGroup implements android.os.Parcelable {
-    ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.util.proto.ProtoOutputStream);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategory();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public final class AidGroup implements android.os.Parcelable {
+    ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public AidGroup(@NonNull java.util.List<java.lang.String>, @Nullable String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public static android.nfc.cardemulation.AidGroup createFromXml(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.util.proto.ProtoOutputStream);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategory();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeAsXml(@NonNull org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.AidGroup> CREATOR;
   }
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class ApduServiceInfo implements android.os.Parcelable {
-    ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getAids();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getCategoryForAid(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @Nullable public String getOffHostSecureElement();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getPrefixAids();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSettingsActivityName();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public java.util.List<java.lang.String> getSubsetAids();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean hasCategory(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean isOnHost();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresScreenOn();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public boolean requiresUnlock();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void resetOffHostSecureElement();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setOffHostSecureElement(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
+    ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.AidGroup> getAidGroups();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getAids();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getCategoryForAid(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public String getOffHostSecureElement();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getPrefixAids();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSettingsActivityName();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getSubsetAids();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
   }
 
-  @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public final class NfcFServiceInfo implements android.os.Parcelable {
-    ctor @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int describeContents();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.content.ComponentName getComponent();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getDescription();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getNfcid2();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getSystemCode();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public String getT3tPmm();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public int getUid();
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicNfcid2(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void setDynamicSystemCode(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE) @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
+  @FlaggedApi("android.nfc.enable_nfc_mainline") public final class NfcFServiceInfo implements android.os.Parcelable {
+    ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public NfcFServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.content.ComponentName getComponent();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getDescription();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getNfcid2();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSystemCode();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getT3tPmm();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicNfcid2(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicSystemCode(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.NfcFServiceInfo> CREATOR;
   }
 
 }
@@ -9843,7 +9843,6 @@
     field public static final int BUGREPORT_FLAG_USE_PREDUMPED_UI_DATA = 1; // 0x1
     field public static final int BUGREPORT_MODE_FULL = 0; // 0x0
     field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1
-    field public static final int BUGREPORT_MODE_ONBOARDING = 7; // 0x7
     field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2
     field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4
     field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3
@@ -10706,13 +10705,13 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_HIBERNATION) public void onGetUnusedAppCount(@NonNull java.util.function.IntConsumer);
     method @BinderThread public abstract void onGrantOrUpgradeDefaultRuntimePermissions(@NonNull Runnable);
     method @Deprecated @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
-    method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String, int);
     method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
     method @Deprecated @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
-    method @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS) @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
+    method @FlaggedApi("android.permission.flags.device_aware_permission_apis") @BinderThread public void onRevokeSelfPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, int, @NonNull Runnable);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -16778,13 +16777,13 @@
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_FAILED = 3; // 0x3
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
     field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
-    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
-    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
+    field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
+    field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
     field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
     field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
     field public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0
     field public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1
-    field @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
+    field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6
     field public static final int SATELLITE_MODEM_STATE_OFF = 4; // 0x4
     field public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5
     field public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4f45691..3bf2cca 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -24,6 +24,7 @@
 import android.Manifest;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -3982,6 +3983,7 @@
      *         the order from most recent to least recent.
      */
     @NonNull
+    @FlaggedApi(Flags.FLAG_APP_START_INFO)
     public List<ApplicationStartInfo> getHistoricalProcessStartReasons(
             @IntRange(from = 0) int maxNum) {
         try {
@@ -4012,6 +4014,7 @@
      */
     @NonNull
     @SystemApi
+    @FlaggedApi(Flags.FLAG_APP_START_INFO)
     @RequiresPermission(Manifest.permission.DUMP)
     public List<ApplicationStartInfo> getExternalHistoricalProcessStartReasons(
             @NonNull String packageName, @IntRange(from = 0) int maxNum) {
@@ -4044,6 +4047,7 @@
      *
      * @throws IllegalArgumentException if executor or listener are null.
      */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO)
     public void setApplicationStartInfoCompletionListener(@NonNull final Executor executor,
             @NonNull final Consumer<ApplicationStartInfo> listener) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -4065,6 +4069,7 @@
     /**
      * Removes the callback set by {@link #setApplicationStartInfoCompletionListener} if there is one.
      */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO)
     public void clearApplicationStartInfoCompletionListener() {
         try {
             getService().clearApplicationStartInfoCompleteListener(mContext.getUserId());
@@ -4089,6 +4094,7 @@
      *                    Will thow {@link java.lang.IllegalArgumentException} if not in range.
      * @param timestampNs Clock monotonic time in nanoseconds of event to be recorded.
      */
+    @FlaggedApi(Flags.FLAG_APP_START_INFO)
     public void addStartInfoTimestamp(@IntRange(
             from = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START,
             to = ApplicationStartInfo.START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) int key,
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index f5fb6ed..a6a57cd 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,6 +38,7 @@
 /**
  * Provide information related to a processes startup.
  */
+@FlaggedApi(Flags.FLAG_APP_START_INFO)
 public final class ApplicationStartInfo implements Parcelable {
 
     /**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7f38b27..ec5effd 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -98,6 +98,7 @@
     ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
     NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
+    ParceledListSlice getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid);
     void updateNotificationChannelGroupForPackage(String pkg, int uid, in NotificationChannelGroup group);
     void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
     void unlockNotificationChannel(String pkg, int uid, String channelId);
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
new file mode 100644
index 0000000..2076e85
--- /dev/null
+++ b/core/java/android/app/activity_manager.aconfig
@@ -0,0 +1,8 @@
+package: "android.app"
+
+flag {
+     namespace: "system_performance"
+     name: "app_start_info"
+     description: "Control collecting of ApplicationStartInfo records and APIs."
+     bug: "247814855"
+}
\ No newline at end of file
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index ed0f872..15bd1dc 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -22,8 +22,10 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.autofill.FillRequest;
+import android.text.InputType;
 import android.text.Spanned;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 import android.view.View;
@@ -2452,7 +2454,7 @@
                     + node.getTextStyle());
             Log.i(TAG, prefix + "  Text color fg: #" + Integer.toHexString(node.getTextColor())
                     + ", bg: #" + Integer.toHexString(node.getTextBackgroundColor()));
-            Log.i(TAG, prefix + "  Input type: " + node.getInputType());
+            Log.i(TAG, prefix + "  Input type: " + getInputTypeString(node.getInputType()));
             Log.i(TAG, prefix + "  Resource id: " + node.getTextIdEntry());
         }
         String webDomain = node.getWebDomain();
@@ -2664,4 +2666,33 @@
             return new AssistStructure[size];
         }
     };
+
+    private static final ArrayMap<Integer, String> INPUT_TYPE_VARIATIONS = new ArrayMap<>();
+    static {
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, "EmailSubject");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS, "PostalAddress");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PERSON_NAME, "PersonName");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_PASSWORD, "Password");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, "VisiblePassword");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_URI, "URI");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS, "WebEmailAddress");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD, "WebPassword");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE, "LongMessage");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE, "ShortMessage");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_MULTI_LINE, "MultiLine");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE, "ImeMultiLine");
+        INPUT_TYPE_VARIATIONS.put(InputType.TYPE_TEXT_VARIATION_FILTER, "Filter");
+    }
+
+    private static String getInputTypeString(int inputType) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(inputType);
+        sb.append("(class=").append(inputType & InputType.TYPE_MASK_CLASS).append(')');
+        for (int variation : INPUT_TYPE_VARIATIONS.keySet()) {
+            if ((variation & inputType) == variation) {
+                sb.append('|').append(INPUT_TYPE_VARIATIONS.get(variation));
+            }
+        }
+        return sb.toString();
+    }
 }
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 4700720..4791a83 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -698,4 +698,34 @@
             return "AmbientLightSensorData(" + sensorName + ", " + sensorType + ")";
         }
     }
+
+    /**
+     * Associate a internal display to a {@link DisplayOffloader}.
+     *
+     * @param displayId the id of the internal display.
+     * @param displayOffloader the {@link DisplayOffloader} that controls offloading ops of internal
+     *                         display whose id is displayId.
+     * @return a {@link DisplayOffloadSession} associated with given displayId and displayOffloader.
+     */
+    public abstract DisplayOffloadSession registerDisplayOffloader(
+            int displayId, DisplayOffloader displayOffloader);
+
+    /** The callbacks that controls the entry & exit of display offloading. */
+    public interface DisplayOffloader {
+        boolean startOffload();
+
+        void stopOffload();
+    }
+
+    /** A session token that associates a internal display with a {@link DisplayOffloader}. */
+    public interface DisplayOffloadSession {
+        /** Provide the display state to use in place of state DOZE. */
+        void setDozeStateOverride(int displayState);
+        /** Returns the associated DisplayOffloader. */
+        DisplayOffloader getDisplayOffloader();
+        /** Returns whether displayoffload supports the given display state. */
+        static boolean isSupportedOffloadState(int displayState) {
+            return Display.isSuspendedState(displayState);
+        }
+    }
 }
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
new file mode 100644
index 0000000..dbc1a4b
--- /dev/null
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.hardware.radio"
+
+flag {
+    name: "hd_radio_improved"
+    namespace: "car_framework"
+    description: "Feature flag for improved HD radio support with less vendor extensions"
+    bug: "280300929"
+}
diff --git a/core/java/android/net/network-policy-restrictions.md b/core/java/android/net/network-policy-restrictions.md
index 04c658c..20f3d74 100644
--- a/core/java/android/net/network-policy-restrictions.md
+++ b/core/java/android/net/network-policy-restrictions.md
@@ -29,8 +29,8 @@
 | **DS**  |  *AL* |  ok  | blk   |  ok  |  ok   |
 | **ON**  | *!AL* | blk  | blk   | blk  | blk   |
 |         |  *DL* | blk  | blk   | blk  | blk   |
-| **DS**  |  *AL* | blk  | blk   |  ok  |  ok   |
-| **OFF** | *!AL* | blk  | blk   |  ok  |  ok   |
+| **DS**  |  *AL* |  ok  | blk   |  ok  |  ok   |
+| **OFF** | *!AL* |  ok  | blk   |  ok  |  ok   |
 |         |  *DL* | blk  | blk   | blk  | blk   |
 
 
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index ada5532..f817fb8 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -32,7 +32,9 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -613,15 +615,35 @@
      */
     public native boolean transactNative(int code, Parcel data, Parcel reply,
             int flags) throws RemoteException;
+
+    /* This list is to hold strong reference to the death recipients that are waiting for the death
+     * of binder that this proxy references. Previously, the death recipients were strongy
+     * referenced from JNI, but that can cause memory leak (b/298374304) when the application has a
+     * strong reference from the death recipient to the proxy object. The JNI reference is now weak.
+     * And this strong reference is to keep death recipients at least until the proxy is GC'ed. */
+    private List<DeathRecipient> mDeathRecipients = Collections.synchronizedList(new ArrayList<>());
+
     /**
      * See {@link IBinder#linkToDeath(DeathRecipient, int)}
      */
-    public native void linkToDeath(DeathRecipient recipient, int flags)
-            throws RemoteException;
+    public void linkToDeath(DeathRecipient recipient, int flags)
+            throws RemoteException {
+        linkToDeathNative(recipient, flags);
+        mDeathRecipients.add(recipient);
+    }
+
     /**
      * See {@link IBinder#unlinkToDeath}
      */
-    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
+    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
+        mDeathRecipients.remove(recipient);
+        return unlinkToDeathNative(recipient, flags);
+    }
+
+    private native void linkToDeathNative(DeathRecipient recipient, int flags)
+            throws RemoteException;
+
+    private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
 
     /**
      * Perform a dump on the remote object
diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java
index f10467f..47ad72f 100644
--- a/core/java/android/os/BugreportParams.java
+++ b/core/java/android/os/BugreportParams.java
@@ -124,6 +124,8 @@
 
     /**
      * Options for a lightweight bugreport intended to be taken for onboarding-related flows.
+     *
+     * @hide
      */
     public static final int BUGREPORT_MODE_ONBOARDING = IDumpstate.BUGREPORT_MODE_ONBOARDING;
 
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 2dbb5da..dae3202 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -76,28 +76,38 @@
      * Enables fs-verity to the owned file under the calling app's private directory. It always uses
      * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
      *
-     * The operation can only succeed when the file is not opened as writable by any process.
+     * <p>For enabling fs-verity to succeed, the device must support fs-verity, the file must be
+     * writable by the app and not already have fs-verity enabled, and the file must not currently
+     * be open for writing by any process. To check whether the device supports fs-verity, use
+     * {@link #isApkVeritySupported()}.
      *
-     * It takes O(file size) time to build the underlying data structure for continuous
+     * <p>It takes O(file size) time to build the underlying data structure for continuous
      * verification. The operation is atomic, i.e. it's either enabled or not, even in case of
      * power failure during or after the call.
      *
-     * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+     * <p>Note for the API users: When the file's authenticity is crucial, the app typical needs to
      * perform a signature check by itself before using the file. The signature is often delivered
      * as a separate file and stored next to the targeting file in the filesystem. The public key of
      * the signer (normally the same app developer) can be put in the APK, and the app can use the
      * public key to verify the signature to the file's actual fs-verity digest (from {@link
-     * #getFsVerityDigest}) before using the file. The exact format is not prescribed by the
+     * #getFsVerityDigest(File)}) before using the file. The exact format is not prescribed by the
      * framework. App developers may choose to use common practices like JCA for the signing and
      * verification, or their own preferred approach.
      *
-     * @param file The file to enable fs-verity. It should be an absolute path.
+     * @param file The file to enable fs-verity. It must represent an absolute path.
+     * @throws IllegalArgumentException If the provided file is not an absolute path.
+     * @throws IOException If the operation failed.
      *
      * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
      */
     @FlaggedApi(Flags.FLAG_FSVERITY_API)
     public void setupFsVerity(@NonNull File file) throws IOException {
         if (!file.isAbsolute()) {
+            // fs-verity is to be enabled by installd, which enforces the validation to the
+            // (untrusted) file path passed from here. To make this less error prone, installd
+            // accepts only absolute path. When a relative path is provided, we fail with an
+            // explicit exception to help developers understand the requirement to use an absolute
+            // path.
             throw new IllegalArgumentException("Expect an absolute path");
         }
         IFsveritySetupAuthToken authToken;
@@ -121,11 +131,12 @@
     }
 
     /**
-     * Returns the fs-verity digest for the owned file under the calling app's
-     * private directory, or null when the file does not have fs-verity enabled.
+     * Returns the fs-verity digest for the owned file under the calling app's private directory, or
+     * null when the file does not have fs-verity enabled (including when fs-verity is not supported
+     * on older devices).
      *
      * @param file The file to measure the fs-verity digest.
-     * @return The fs-verity digeset in byte[], null if none.
+     * @return The fs-verity digest in byte[], null if none.
      * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
      */
     @FlaggedApi(Flags.FLAG_FSVERITY_API)
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 94d8516..6a82f6d 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -959,8 +959,8 @@
                 mKeyphraseMetadata = new KeyphraseMetadata(1, mText, fakeSupportedLocales,
                         AlwaysOnHotwordDetector.RECOGNITION_MODE_VOICE_TRIGGER);
             }
-            notifyStateChangedLocked();
         }
+        notifyStateChanged(availability);
     }
 
     /**
@@ -1370,8 +1370,8 @@
 
             mAvailability = STATE_INVALID;
             mIsAvailabilityOverriddenByTestApi = false;
-            notifyStateChangedLocked();
         }
+        notifyStateChanged(STATE_INVALID);
         super.destroy();
     }
 
@@ -1401,6 +1401,8 @@
      */
     // TODO(b/281608561): remove the enrollment flow from AlwaysOnHotwordDetector
     void onSoundModelsChanged() {
+        boolean notifyError = false;
+
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID
                     || mAvailability == STATE_HARDWARE_UNAVAILABLE
@@ -1441,6 +1443,9 @@
                     // calling stopRecognition where there is no started session.
                     Log.w(TAG, "Failed to stop recognition after enrollment update: code="
                             + result);
+
+                    // Execute a refresh availability task - which should then notify of a change.
+                    new RefreshAvailabilityTask().execute();
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to stop recognition after enrollment update", e);
                     if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
@@ -1449,14 +1454,14 @@
                                         + Log.getStackTraceString(e),
                                 FailureSuggestedAction.RECREATE_DETECTOR));
                     } else {
-                        updateAndNotifyStateChangedLocked(STATE_ERROR);
+                        notifyError = true;
                     }
-                    return;
                 }
             }
+        }
 
-            // Execute a refresh availability task - which should then notify of a change.
-            new RefreshAvailabilityTask().execute();
+        if (notifyError) {
+            updateAndNotifyStateChanged(STATE_ERROR);
         }
     }
 
@@ -1572,10 +1577,11 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void updateAndNotifyStateChangedLocked(int availability) {
-        updateAvailabilityLocked(availability);
-        notifyStateChangedLocked();
+    private void updateAndNotifyStateChanged(int availability) {
+        synchronized (mLock) {
+            updateAvailabilityLocked(availability);
+        }
+        notifyStateChanged(availability);
     }
 
     @GuardedBy("mLock")
@@ -1589,17 +1595,17 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void notifyStateChangedLocked() {
+    private void notifyStateChanged(int newAvailability) {
         Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
-        message.arg1 = mAvailability;
+        message.arg1 = newAvailability;
         message.sendToTarget();
     }
 
-    @GuardedBy("mLock")
     private void sendUnknownFailure(String failureMessage) {
-        // update but do not call onAvailabilityChanged callback for STATE_ERROR
-        updateAvailabilityLocked(STATE_ERROR);
+        synchronized (mLock) {
+            // update but do not call onAvailabilityChanged callback for STATE_ERROR
+            updateAvailabilityLocked(STATE_ERROR);
+        }
         Message.obtain(mHandler, MSG_DETECTION_UNKNOWN_FAILURE, failureMessage).sendToTarget();
     }
 
@@ -1802,19 +1808,17 @@
                             availability = STATE_KEYPHRASE_UNENROLLED;
                         }
                     }
-                    updateAndNotifyStateChangedLocked(availability);
                 }
+                updateAndNotifyStateChanged(availability);
             } catch (Exception e) {
                 // Any exception here not caught will crash the process because AsyncTask does not
                 // bubble up the exceptions to the client app, so we must propagate it to the app.
                 Slog.w(TAG, "Failed to refresh availability", e);
-                synchronized (mLock) {
-                    if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
-                        sendUnknownFailure(
-                                "Failed to refresh availability: " + Log.getStackTraceString(e));
-                    } else {
-                        updateAndNotifyStateChangedLocked(STATE_ERROR);
-                    }
+                if (CompatChanges.isChangeEnabled(SEND_ON_FAILURE_FOR_ASYNC_EXCEPTIONS)) {
+                    sendUnknownFailure(
+                            "Failed to refresh availability: " + Log.getStackTraceString(e));
+                } else {
+                    updateAndNotifyStateChanged(STATE_ERROR);
                 }
             }
 
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
new file mode 100644
index 0000000..46fa501
--- /dev/null
+++ b/core/java/android/text/ClientFlags.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.text;
+
+import com.android.text.flags.Flags;
+
+/**
+ * An aconfig feature flags that can be accessible from application process without
+ * ContentProvider IPCs.
+ *
+ * When you add new flags, you have to add flag string to {@link TextFlags#TEXT_ACONFIGS_FLAGS}.
+ *
+ * @hide
+ */
+public class ClientFlags {
+
+    /**
+     * @see Flags#deprecateFontsXml()
+     */
+    public static boolean deprecateFontsXml() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_DEPRECATE_FONTS_XML);
+    }
+
+    /**
+     * @see Flags#noBreakNoHyphenationSpan()
+     */
+    public static boolean noBreakNoHyphenationSpan() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN);
+    }
+
+    /**
+     * @see Flags#phraseStrictFallback()
+     */
+    public static boolean phraseStrictFallback() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
+    }
+
+    /**
+     * @see Flags#useBoundsForWidth()
+     */
+    public static boolean useBoundsForWidth() {
+        return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
+    }
+}
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 4be6a8d..536e3cc 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -16,6 +16,11 @@
 
 package android.text;
 
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+
+import com.android.text.flags.Flags;
+
 /**
  * Flags in the "text" namespace.
  *
@@ -46,4 +51,28 @@
      */
     public static final boolean ENABLE_NEW_CONTEXT_MENU_DEFAULT = true;
 
+    /**
+     * List of text flags to be transferred to the application process.
+     */
+    public static final String[] TEXT_ACONFIGS_FLAGS = {
+            Flags.FLAG_DEPRECATE_FONTS_XML,
+            Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
+            Flags.FLAG_PHRASE_STRICT_FALLBACK,
+            Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
+    };
+
+    /**
+     * Get a key for the feature flag.
+     */
+    public static String getKeyForFlag(@NonNull String flag) {
+        return "text__" + flag;
+    }
+
+    /**
+     * Return true if the feature flag is enabled.
+     */
+    public static boolean isFeatureEnabled(@NonNull String flag) {
+        return AppGlobals.getIntCoreSetting(
+                getKeyForFlag(flag), 0 /* aconfig is false by default */) != 0;
+    }
 }
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index a46136a..31d759e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -264,6 +264,16 @@
     }
 
     /**
+     * Called when a display hotplug event with connection error is received.
+     *
+     * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
+     * timebase.
+     * @param connectionError the hotplug connection error code.
+     */
+    public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+    }
+
+    /**
      * Called when a display mode changed event is received.
      *
      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
@@ -345,6 +355,11 @@
         onHotplug(timestampNanos, physicalDisplayId, connected);
     }
 
+    @SuppressWarnings("unused")
+    private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) {
+        onHotplugConnectionError(timestampNanos, connectionError);
+    }
+
     // Called from native code.
     @SuppressWarnings("unused")
     private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0ba5d06..f421351 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3858,9 +3858,7 @@
                 mPendingTransitions.clear();
             }
 
-            if (mActiveSurfaceSyncGroup != null) {
-                mActiveSurfaceSyncGroup.markSyncReady();
-            }
+            handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
         } else if (cancelAndRedraw) {
             mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener
                 ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason()
@@ -3874,8 +3872,8 @@
                 }
                 mPendingTransitions.clear();
             }
-            if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) {
-                mActiveSurfaceSyncGroup.markSyncReady();
+            if (!performDraw(mActiveSurfaceSyncGroup)) {
+                handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
             }
         }
 
@@ -3890,6 +3888,7 @@
             mReportNextDraw = false;
             mLastReportNextDrawReason = null;
             mActiveSurfaceSyncGroup = null;
+            mHasPendingTransactions = false;
             mSyncBuffer = false;
             if (isInWMSRequestedSync()) {
                 mWmsRequestSyncGroup.markSyncReady();
@@ -4688,7 +4687,8 @@
             return false;
         }
 
-        final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
+        final boolean fullRedrawNeeded =
+                mFullRedrawNeeded || surfaceSyncGroup != null || mHasPendingTransactions;
         mFullRedrawNeeded = false;
 
         mIsDrawing = true;
@@ -4718,8 +4718,15 @@
             mAttachInfo.mPendingAnimatingRenderNodes.clear();
         }
 
-        if (mReportNextDraw) {
+        final Transaction pendingTransaction;
+        if (!usingAsyncReport && mHasPendingTransactions) {
+            pendingTransaction = new Transaction();
+            pendingTransaction.merge(mPendingTransaction);
+        } else {
+            pendingTransaction = null;
+        }
 
+        if (mReportNextDraw) {
             // if we're using multi-thread renderer, wait for the window frame draws
             if (mWindowDrawCountDown != null) {
                 try {
@@ -4741,9 +4748,7 @@
             if (mSurfaceHolder != null && mSurface.isValid()) {
                 usingAsyncReport = true;
                 SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
-                    if (surfaceSyncGroup != null) {
-                        surfaceSyncGroup.markSyncReady();
-                    }
+                    handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
                 });
 
                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4756,15 +4761,27 @@
             }
         }
 
-        if (surfaceSyncGroup != null && !usingAsyncReport) {
-            surfaceSyncGroup.markSyncReady();
+        if (!usingAsyncReport) {
+            handleSyncRequestWhenNoAsyncDraw(surfaceSyncGroup, pendingTransaction);
         }
+
         if (mPerformContentCapture) {
             performContentCaptureInitialReport();
         }
         return true;
     }
 
+    private void handleSyncRequestWhenNoAsyncDraw(SurfaceSyncGroup surfaceSyncGroup,
+            @Nullable Transaction pendingTransaction) {
+        if (surfaceSyncGroup != null) {
+            if (pendingTransaction != null) {
+                surfaceSyncGroup.addTransaction(pendingTransaction);
+            }
+            surfaceSyncGroup.markSyncReady();
+        } else if (pendingTransaction != null) {
+            pendingTransaction.apply();
+        }
+    }
     /**
      * Checks (and caches) if content capture is enabled for this context.
      */
@@ -4850,8 +4867,8 @@
         }
     }
 
-    private boolean draw(boolean fullRedrawNeeded,
-            @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) {
+    private boolean draw(boolean fullRedrawNeeded, @Nullable SurfaceSyncGroup activeSyncGroup,
+            boolean syncBuffer) {
         Surface surface = mSurface;
         if (!surface.isValid()) {
             return false;
@@ -4995,12 +5012,11 @@
                         mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
                     }
                 } else if (mHasPendingTransactions) {
-                    // Register a calback if there's no sync involved but there were calls to
+                    // Register a callback if there's no sync involved but there were calls to
                     // applyTransactionOnDraw. If there is a sync involved, the sync callback will
                     // handle merging the pending transaction.
                     registerCallbackForPendingTransactions();
                 }
-                mHasPendingTransactions = false;
 
                 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
             } else {
@@ -8977,13 +8993,7 @@
             mAdded = false;
             AnimationHandler.removeRequestor(this);
         }
-        if (mActiveSurfaceSyncGroup != null) {
-            mActiveSurfaceSyncGroup.markSyncReady();
-            mActiveSurfaceSyncGroup = null;
-        }
-        if (mHasPendingTransactions) {
-            mPendingTransaction.apply();
-        }
+        handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mPendingTransaction);
         WindowManagerGlobal.getInstance().doRemoveView(this);
     }
 
@@ -11502,9 +11512,7 @@
             Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
         }
 
-        Transaction t = new Transaction();
-        t.merge(mPendingTransaction);
-
+        surfaceSyncGroup.addTransaction(mPendingTransaction);
         mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
             @Override
             public void onFrameDraw(long frame) {
@@ -11518,7 +11526,6 @@
                                     + frame + ".");
                 }
 
-                mergeWithNextTransaction(t, frame);
                 // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
                 // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
                 // any blast sync or commit callback, and the code should directly call
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a0d0656..2c41330 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -103,6 +103,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.BoringLayout;
+import android.text.ClientFlags;
 import android.text.DynamicLayout;
 import android.text.Editable;
 import android.text.GetChars;
@@ -1634,7 +1635,7 @@
         }
 
         if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
-            mUseBoundsForWidth = false;  // TODO: Connect to the flag.
+            mUseBoundsForWidth = ClientFlags.useBoundsForWidth();
         } else {
             mUseBoundsForWidth = false;
         }
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 9b10a7f..932608a3 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,6 +16,8 @@
 
 package android.window;
 
+import static android.view.WindowManager.transitTypeToString;
+
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.WindowConfiguration;
@@ -88,6 +90,11 @@
         this(type, triggerTask, null /* pipTask */, remoteTransition, displayChange, flags);
     }
 
+    /** @hide */
+    String typeToString() {
+        return transitTypeToString(mType);
+    }
+
     /** Requested change to a display. */
     @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
     public static class DisplayChange implements Parcelable {
@@ -263,7 +270,7 @@
         };
 
         @DataClass.Generated(
-                time = 1693425051905L,
+                time = 1695667226050L,
                 codegenVersion = "1.0.23",
                 sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
                 inputSignatures = "private final  int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate  int mStartRotation\nprivate  int mEndRotation\nprivate  boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -298,11 +305,11 @@
      * @param type
      *   The type of the transition being requested.
      * @param triggerTask
-     *   If non-null, If non-null, the task containing the activity whose lifecycle change (start or
+     *   If non-null, the task containing the activity whose lifecycle change (start or
      *   finish) has caused this transition to occur.
      * @param pipTask
-     *   If non-null, If non-null, the task containing the activity whose lifecycle change (start or
-     *   finish) has caused this transition to occur.
+     *   If non-null, the task containing the pip activity that participates in this
+     *   transition.
      * @param remoteTransition
      *   If non-null, a remote-transition associated with the source of this transition.
      * @param displayChange
@@ -431,7 +438,7 @@
         // String fieldNameToString() { ... }
 
         return "TransitionRequestInfo { " +
-                "type = " + mType + ", " +
+                "type = " + typeToString() + ", " +
                 "triggerTask = " + mTriggerTask + ", " +
                 "pipTask = " + mPipTask + ", " +
                 "remoteTransition = " + mRemoteTransition + ", " +
@@ -506,10 +513,10 @@
     };
 
     @DataClass.Generated(
-            time = 1693425051928L,
+            time = 1695667226088L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
-            inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final  int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+            inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mPipTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final  int mFlags\n  java.lang.String typeToString()\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 0d1871d..663067c 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -203,14 +203,22 @@
         }
         return false;
     }
-    
+
+    /**
+     * Direct reflection of {@link Intent#ACTION_PACKAGE_CHANGED
+     * Intent.ACTION_PACKAGE_CHANGED} being received, this callback
+     * has extras passed in.
+     */
+    public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+    }
+
     public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
         return false;
     }
 
     public void onHandleUserStop(Intent intent, int userHandle) {
     }
-    
+
     public void onUidRemoved(int uid) {
     }
     
@@ -238,21 +246,34 @@
     }
 
     /**
+     * Called when a package disappears with extras passed in.
+     */
+    public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+    }
+
+    /**
      * Called when a package appears for any reason.
      */
     public void onPackageAppeared(String packageName, int reason) {
     }
 
+
+    /**
+     * Called when a package appears with extras passed in.
+     */
+    public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+    }
+
     /**
      * Called when an existing package is updated or its disabled state changes.
      */
     public void onPackageModified(@NonNull String packageName) {
     }
-    
+
     public boolean didSomePackagesChange() {
         return mSomePackagesChanged;
     }
-    
+
     public int isPackageAppearing(String packageName) {
         if (mAppearingPackages != null) {
             for (int i=mAppearingPackages.length-1; i>=0; i--) {
@@ -381,6 +402,7 @@
                     mChangeType = PACKAGE_PERMANENT_CHANGE;
                     onPackageAdded(pkg, uid);
                 }
+                onPackageAppearedWithExtras(pkg, intent.getExtras());
                 onPackageAppeared(pkg, mChangeType);
             }
         } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
@@ -403,6 +425,7 @@
                         onPackageRemovedAllUsers(pkg, uid);
                     }
                 }
+                onPackageDisappearedWithExtras(pkg, intent.getExtras());
                 onPackageDisappeared(pkg, mChangeType);
             }
         } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -417,6 +440,7 @@
                 if (onPackageChanged(pkg, uid, mModifiedComponents)) {
                     mSomePackagesChanged = true;
                 }
+                onPackageChangedWithExtras(pkg, intent.getExtras());
                 onPackageModified(pkg);
             }
         } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index d503904..7a87c3a 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -16,9 +16,11 @@
 
 package com.android.internal.display;
 
+import android.annotation.SuppressLint;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.net.Uri;
@@ -54,8 +56,7 @@
     private static final int MSG_RUN_UPDATE = 1;
 
     // The tolerance within which we consider brightness values approximately equal to eachother.
-    // This value is approximately 1/3 of the smallest possible brightness value.
-    public static final float EPSILON = 0.001f;
+    public static final float EPSILON = 0.0001f;
 
     private static int sBrightnessUpdateCount = 1;
 
@@ -284,6 +285,74 @@
     }
 
     /**
+     * Converts between the int brightness setting and the float brightness system. The int
+     * brightness setting is between 0-255 and matches the brightness slider - e.g. 128 is 50% on
+     * the slider. Accounts for special values such as OFF and invalid values. Accounts for
+     * brightness limits; the maximum value here represents the max value allowed on the slider.
+     */
+    @VisibleForTesting
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public float brightnessIntSettingToFloat(int brightnessInt) {
+        if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
+            return PowerManager.BRIGHTNESS_OFF_FLOAT;
+        } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
+            return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        } else {
+            final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+            final float maxInt = PowerManager.BRIGHTNESS_ON;
+
+            // Normalize to the range [0, 1]
+            float userPerceptionBrightness = MathUtils.norm(minInt, maxInt, brightnessInt);
+
+            // Convert from user-perception to linear scale
+            float linearBrightness = BrightnessUtils.convertGammaToLinear(userPerceptionBrightness);
+
+            // Interpolate to the range [0, currentlyAllowedMax]
+            final Display display = mContext.getDisplay();
+            if (display == null) {
+                return PowerManager.BRIGHTNESS_INVALID_FLOAT;
+            }
+            final BrightnessInfo info = display.getBrightnessInfo();
+            return MathUtils.lerp(info.brightnessMinimum, info.brightnessMaximum, linearBrightness);
+        }
+    }
+
+    /**
+     * Translates specified value from the float brightness system to the setting int brightness
+     * system. The value returned is between 0-255 and matches the brightness slider - e.g. 128 is
+     * 50% on the slider. Accounts for special values such as OFF and invalid values. Accounts for
+     * brightness limits; the maximum value here represents the max value currently allowed on
+     * the slider.
+     */
+    @VisibleForTesting
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public int brightnessFloatToIntSetting(float brightnessFloat) {
+        if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
+            return PowerManager.BRIGHTNESS_OFF;
+        } else if (Float.isNaN(brightnessFloat)) {
+            return PowerManager.BRIGHTNESS_INVALID;
+        } else {
+            // Normalize to the range [0, 1]
+            final Display display = mContext.getDisplay();
+            if (display == null) {
+                return PowerManager.BRIGHTNESS_INVALID;
+            }
+            final BrightnessInfo info = display.getBrightnessInfo();
+            float linearBrightness =
+                    MathUtils.norm(info.brightnessMinimum, info.brightnessMaximum, brightnessFloat);
+
+            // Convert from linear to user-perception scale
+            float userPerceptionBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
+
+            // Interpolate to the range [0, 255]
+            final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
+            final float maxInt = PowerManager.BRIGHTNESS_ON;
+            float intBrightness = MathUtils.lerp(minInt, maxInt, userPerceptionBrightness);
+            return Math.round(intBrightness);
+        }
+    }
+
+    /**
      * Encapsulates a brightness change event and contains logic for synchronizing the appropriate
      * settings for the specified brightness change.
      */
@@ -421,14 +490,14 @@
             if (mSourceType == TYPE_INT) {
                 return (int) mBrightness;
             }
-            return brightnessFloatToInt(mBrightness);
+            return brightnessFloatToIntSetting(mBrightness);
         }
 
         private float getBrightnessAsFloat() {
             if (mSourceType == TYPE_FLOAT) {
                 return mBrightness;
             }
-            return brightnessIntToFloat((int) mBrightness);
+            return brightnessIntSettingToFloat((int) mBrightness);
         }
 
         private String toStringLabel(int type, float brightness) {
diff --git a/services/core/java/com/android/server/display/BrightnessUtils.java b/core/java/com/android/internal/display/BrightnessUtils.java
similarity index 96%
rename from services/core/java/com/android/server/display/BrightnessUtils.java
rename to core/java/com/android/internal/display/BrightnessUtils.java
index 84fa0cc..82b506b 100644
--- a/services/core/java/com/android/server/display/BrightnessUtils.java
+++ b/core/java/com/android/internal/display/BrightnessUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.display;
+package com.android.internal.display;
 
 import android.util.MathUtils;
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c6f5086..7eeac29 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -154,6 +154,7 @@
 
     void addQsTile(in ComponentName tile);
     void remQsTile(in ComponentName tile);
+    void setQsTiles(in String[] tiles);
     void clickQsTile(in ComponentName tile);
     void handleSystemKey(in KeyEvent key);
 
diff --git a/core/java/com/android/internal/widget/CallLayout.java b/core/java/com/android/internal/widget/CallLayout.java
index acb0e44..89f4659 100644
--- a/core/java/com/android/internal/widget/CallLayout.java
+++ b/core/java/com/android/internal/widget/CallLayout.java
@@ -49,6 +49,7 @@
     private CachingIconView mIcon;
     private CachingIconView mConversationIconBadgeBg;
     private TextView mConversationText;
+    private boolean mSetDataAsyncEnabled = false;
 
     public CallLayout(@NonNull Context context) {
         super(context);
@@ -83,7 +84,8 @@
         });
     }
 
-    private void updateCallLayout() {
+    @NonNull
+    private Icon getConversationIcon() {
         CharSequence callerName = "";
         String symbol = "";
         Icon icon = null;
@@ -98,8 +100,7 @@
         if (icon == null) {
             icon = mPeopleHelper.createAvatarSymbol(callerName, symbol, mLayoutColor);
         }
-        // TODO(b/179178086): crop/clip the icon to a circle?
-        mConversationIconView.setImageIcon(icon);
+        return icon;
     }
 
     @RemotableViewMethod
@@ -123,10 +124,38 @@
     /**
      * Set the notification extras so that this layout has access
      */
-    @RemotableViewMethod
+    @RemotableViewMethod(asyncImpl = "setDataAsync")
     public void setData(Bundle extras) {
-        setUser(extras.getParcelable(Notification.EXTRA_CALL_PERSON, android.app.Person.class));
-        updateCallLayout();
+        final Person person = getPerson(extras);
+        setUser(person);
+
+        final Icon icon = getConversationIcon();
+        mConversationIconView.setImageIcon(icon);
+    }
+
+
+    public void setSetDataAsyncEnabled(boolean setDataAsyncEnabled) {
+        mSetDataAsyncEnabled = setDataAsyncEnabled;
+    }
+
+    /**
+     * Async implementation for setData
+     */
+    public Runnable setDataAsync(Bundle extras) {
+        if (!mSetDataAsyncEnabled) {
+            return () -> setData(extras);
+        }
+
+        final Person person = getPerson(extras);
+        setUser(person);
+
+        final Icon conversationIcon = getConversationIcon();
+        return mConversationIconView.setImageIconAsync(conversationIcon);
+    }
+
+    @Nullable
+    private Person getPerson(Bundle extras) {
+        return extras.getParcelable(Notification.EXTRA_CALL_PERSON, Person.class);
     }
 
     private void setUser(Person user) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index ad196c0..9ed4155 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -15,7 +15,19 @@
     ],
 }
 
-cc_library_shared {
+soong_config_module_type {
+    name: "cc_library_shared_for_libandroid_runtime",
+    module_type: "cc_library_shared",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_binder_death_recipient_weak_from_jni",
+    ],
+    properties: [
+        "cflags",
+    ],
+}
+
+cc_library_shared_for_libandroid_runtime {
     name: "libandroid_runtime",
     host_supported: true,
     cflags: [
@@ -46,6 +58,12 @@
         },
     },
 
+    soong_config_variables: {
+        release_binder_death_recipient_weak_from_jni: {
+            cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
+        },
+    },
+
     cpp_std: "gnu++20",
 
     srcs: [
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 041f9c7..bfd80a9e 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -17,19 +17,8 @@
 #define LOG_TAG "JavaBinder"
 //#define LOG_NDEBUG 0
 
-#include "android_os_Parcel.h"
 #include "android_util_Binder.h"
 
-#include <atomic>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <mutex>
-#include <stdio.h>
-#include <string>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
 #include <android-base/stringprintf.h>
 #include <binder/BpBinder.h>
 #include <binder/IInterface.h>
@@ -40,7 +29,16 @@
 #include <binder/Stability.h>
 #include <binderthreadstate/CallerUtils.h>
 #include <cutils/atomic.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include <utils/KeyedVector.h>
 #include <utils/List.h>
 #include <utils/Log.h>
@@ -48,10 +46,11 @@
 #include <utils/SystemClock.h>
 #include <utils/threads.h>
 
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
+#include <atomic>
+#include <mutex>
+#include <string>
 
+#include "android_os_Parcel.h"
 #include "core_jni_helpers.h"
 
 //#undef ALOGV
@@ -553,14 +552,48 @@
 };
 
 // ----------------------------------------------------------------------------
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+#if __BIONIC__
+#include <android/api-level.h>
+static bool target_sdk_is_at_least_vic() {
+    return android_get_application_target_sdk_version() >= __ANDROID_API_V__;
+}
+#else
+static constexpr bool target_sdk_is_at_least_vic() {
+    // If not built for Android (i.e. glibc host), follow the latest behavior as there's no compat
+    // requirement there.
+    return true;
+}
+#endif // __BIONIC__
+#endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
 
 class JavaDeathRecipient : public IBinder::DeathRecipient
 {
 public:
     JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
-        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
-          mObjectWeak(NULL), mList(list)
-    {
+          : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+        // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
+        // to the death recipient objects. This is to prevent the memory leak which can happen when
+        // the death recipient object internally has a strong reference to the proxy object. Under
+        // the old behavior, you were unable to kill the binder service by dropping all references
+        // to the proxy object - because it is still strong referenced from JNI (here). The only way
+        // to cut the strong reference was to call unlinkDeath(), but it was easy to forget.
+        //
+        // Now, the strong reference to the death recipient is held in the Java-side proxy object.
+        // See BinderProxy.mDeathRecipients. From JNI, only the weak reference is kept. An
+        // implication of this is that you may not receive binderDied() if you drop all references
+        // to the proxy object before the service dies. This should be okay for most cases because
+        // you normally are not interested in the death of a binder service which you don't have any
+        // reference to. If however you want to get binderDied() regardless of the proxy object's
+        // lifecycle, keep a strong reference to the death recipient object by yourself.
+#ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
+        if (target_sdk_is_at_least_vic()) {
+            mObjectWeak = env->NewWeakGlobalRef(object);
+        } else
+#endif
+        {
+            mObject = env->NewGlobalRef(object);
+        }
         // These objects manage their own lifetimes so are responsible for final bookkeeping.
         // The list holds a strong reference to this object.
         LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
@@ -573,26 +606,49 @@
     void binderDied(const wp<IBinder>& who)
     {
         LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
-        if (mObject != NULL) {
-            JNIEnv* env = javavm_to_jnienv(mVM);
-            ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
-            env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
-                                      gBinderProxyOffsets.mSendDeathNotice, mObject,
-                                      jBinderProxy.get());
-            if (env->ExceptionCheck()) {
-                jthrowable excep = env->ExceptionOccurred();
-                binder_report_exception(env, excep,
-                                        "*** Uncaught exception returned from death notification!");
-            }
+        if (mObject == NULL && mObjectWeak == NULL) {
+            return;
+        }
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
 
-            // Serialize with our containing DeathRecipientList so that we can't
-            // delete the global ref on mObject while the list is being iterated.
+        // Hold a local reference to the recipient. This may fail if the recipient is weakly
+        // referenced, in which case we can't deliver the death notice.
+        ScopedLocalRef<jobject> jRecipient(env,
+                                           env->NewLocalRef(mObject != NULL ? mObject
+                                                                            : mObjectWeak));
+        if (jRecipient.get() == NULL) {
+            ALOGW("Binder died, but death recipient is already garbage collected. If your target "
+                  "sdk level is at or above 35, this can happen when you dropped all references to "
+                  "the binder service before it died. If you want to get a death notice for a "
+                  "binder service which you have no reference to, keep a strong reference to the "
+                  "death recipient by yourself.");
+            return;
+        }
+
+        if (mFired) {
+            ALOGW("Received multiple death notices for the same binder object. Binder driver bug?");
+            return;
+        }
+        mFired = true;
+
+        env->CallStaticVoidMethod(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mSendDeathNotice,
+                                  jRecipient.get(), jBinderProxy.get());
+        if (env->ExceptionCheck()) {
+            jthrowable excep = env->ExceptionOccurred();
+            binder_report_exception(env, excep,
+                                    "*** Uncaught exception returned from death notification!");
+        }
+
+        // Demote from strong ref (if exists) to weak after binderDied() has been delivered, to
+        // allow the DeathRecipient and BinderProxy to be GC'd if no longer needed. Do this in sync
+        // with our containing DeathRecipientList so that we can't delete the global ref on mObject
+        // while the list is being iterated.
+        if (mObject != NULL) {
             sp<DeathRecipientList> list = mList.promote();
             if (list != NULL) {
                 AutoMutex _l(list->lock());
 
-                // Demote from strong ref to weak after binderDied() has been delivered,
-                // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
                 mObjectWeak = env->NewWeakGlobalRef(mObject);
                 env->DeleteGlobalRef(mObject);
                 mObject = NULL;
@@ -659,9 +715,19 @@
 
 private:
     JavaVM* const mVM;
-    jobject mObject;  // Initial strong ref to Java-side DeathRecipient. Cleared on binderDied().
-    jweak mObjectWeak; // Weak ref to the same Java-side DeathRecipient after binderDied().
+
+    // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
+    // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
+    // a weak reference (mObjectWeak).
+    // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
+    // always). Instead, the strong reference to the Java-side DeathRecipient is made in
+    // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
+    jobject mObject;
+    jweak mObjectWeak;
     wp<DeathRecipientList> mList;
+
+    // Whether binderDied was called or not.
+    bool mFired = false;
 };
 
 // ----------------------------------------------------------------------------
@@ -1435,17 +1501,19 @@
 
 // ----------------------------------------------------------------------------
 
+// clang-format off
 static const JNINativeMethod gBinderProxyMethods[] = {
      /* name, signature, funcPtr */
     {"pingBinder",          "()Z", (void*)android_os_BinderProxy_pingBinder},
     {"isBinderAlive",       "()Z", (void*)android_os_BinderProxy_isBinderAlive},
     {"getInterfaceDescriptor", "()Ljava/lang/String;", (void*)android_os_BinderProxy_getInterfaceDescriptor},
     {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
-    {"linkToDeath",         "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
-    {"unlinkToDeath",       "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+    {"linkToDeathNative",   "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
+    {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
     {"getNativeFinalizer",  "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
     {"getExtension",        "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
 };
+// clang-format on
 
 const char* const kBinderProxyPathName = "android/os/BinderProxy";
 
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 69fc515..41c65ae 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -38,6 +38,7 @@
 
     jmethodID dispatchVsync;
     jmethodID dispatchHotplug;
+    jmethodID dispatchHotplugConnectionError;
     jmethodID dispatchModeChanged;
     jmethodID dispatchFrameRateOverrides;
 
@@ -89,6 +90,7 @@
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
                        VsyncEventData vsyncEventData) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
+    void dispatchHotplugConnectionError(nsecs_t timestamp, int errorCode) override;
     void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
                              nsecs_t renderPeriod) override;
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
@@ -230,6 +232,22 @@
     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
 }
 
+void NativeDisplayEventReceiver::dispatchHotplugConnectionError(nsecs_t timestamp,
+                                                                int connectionError) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking hotplug dispatchHotplugConnectionError handler.", this);
+        env->CallVoidMethod(receiverObj.get(),
+                            gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError,
+                            timestamp, connectionError);
+        ALOGV("receiver %p ~ Returned from hotplug dispatchHotplugConnectionError handler.", this);
+    }
+
+    mMessageQueue->raiseAndClearException(env, "dispatchHotplugConnectionError");
+}
+
 void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
                                                      int32_t modeId, nsecs_t renderPeriod) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -354,8 +372,12 @@
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
-    gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
-            gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
+    gDisplayEventReceiverClassInfo.dispatchHotplug =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug",
+                             "(JJZ)V");
+    gDisplayEventReceiverClassInfo.dispatchHotplugConnectionError =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
+                             "dispatchHotplugConnectionError", "(JI)V");
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
                              "(JJIJ)V");
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a1adfc3..c00a776f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4830,7 +4830,7 @@
     <string translatable="false" name="config_deviceSpecificInputMethodManagerService"></string>
 
     <!-- Component name of media projection permission dialog -->
-    <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.mediaprojection.MediaProjectionPermissionActivity</string>
+    <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
 
     <!-- Corner radius of system dialogs -->
     <dimen name="config_dialogCornerRadius">28dp</dimen>
diff --git a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
index e082c25..c7eddabe 100644
--- a/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
+++ b/core/tests/packagemonitortests/src/com/android/internal/content/PackageMonitorTest.java
@@ -17,7 +17,6 @@
 package com.android.internal.content;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 
@@ -36,6 +36,7 @@
 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;
 
@@ -45,6 +46,7 @@
 @RunWith(AndroidJUnit4.class)
 public class PackageMonitorTest {
     private static final String FAKE_PACKAGE_NAME = "com.android.internal.content.fakeapp";
+    private static final String FAKE_EXTRA_REASON = "android.intent.extra.fakereason";
     private static final int FAKE_PACKAGE_UID = 123;
     private static final int FAKE_USER_ID = 0;
     private static final int WAIT_CALLBACK_CALLED_IN_MS = 300;
@@ -245,6 +247,7 @@
         intent.setData(Uri.fromParts("package", FAKE_PACKAGE_NAME, null));
         intent.putExtra(Intent.EXTRA_USER_HANDLE, FAKE_USER_ID);
         intent.putExtra(Intent.EXTRA_UID, FAKE_PACKAGE_UID);
+        intent.putExtra(Intent.EXTRA_REASON, FAKE_EXTRA_REASON);
         String [] packageList = new String[]{FAKE_PACKAGE_NAME};
         intent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, packageList);
         spyPackageMonitor.doHandlePackageEvent(intent);
@@ -253,6 +256,20 @@
         verify(spyPackageMonitor, times(1))
                 .onPackageChanged(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID), eq(packageList));
         verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1)).onPackageChangedWithExtras(eq(FAKE_PACKAGE_NAME),
+                argumentCaptor.capture());
+
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REASON))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REASON));
+
         verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
         verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
     }
@@ -272,6 +289,21 @@
         verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
         verify(spyPackageMonitor, times(1))
                 .onPackageUpdateStarted(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+                argumentCaptor.capture());
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
         verify(spyPackageMonitor, times(1))
                 .onPackageDisappeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
         verify(spyPackageMonitor, times(1)).onFinishPackageChanges();
@@ -295,6 +327,21 @@
                 .onPackageRemoved(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
         verify(spyPackageMonitor, times(1))
                 .onPackageRemovedAllUsers(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1)).onPackageDisappearedWithExtras(eq(FAKE_PACKAGE_NAME),
+                argumentCaptor.capture());
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REMOVED_FOR_ALL_USERS));
+
         verify(spyPackageMonitor, times(1)).onPackageDisappeared(eq(FAKE_PACKAGE_NAME),
                 eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
         verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -316,6 +363,19 @@
         verify(spyPackageMonitor, times(1))
                 .onPackageUpdateFinished(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
         verify(spyPackageMonitor, times(1)).onPackageModified(eq(FAKE_PACKAGE_NAME));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+                argumentCaptor.capture());
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
         verify(spyPackageMonitor, times(1))
                 .onPackageAppeared(eq(FAKE_PACKAGE_NAME), eq(PackageMonitor.PACKAGE_UPDATING));
         verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
@@ -336,6 +396,19 @@
         verify(spyPackageMonitor, times(1)).onBeginPackageChanges();
         verify(spyPackageMonitor, times(1))
                 .onPackageAdded(eq(FAKE_PACKAGE_NAME), eq(FAKE_PACKAGE_UID));
+
+        ArgumentCaptor<Bundle> argumentCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(spyPackageMonitor, times(1)).onPackageAppearedWithExtras(eq(FAKE_PACKAGE_NAME),
+                argumentCaptor.capture());
+        Bundle capturedExtras = argumentCaptor.getValue();
+        Bundle expectedExtras = intent.getExtras();
+        assertThat(capturedExtras.getInt(Intent.EXTRA_USER_HANDLE))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_USER_HANDLE));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_UID))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_UID));
+        assertThat(capturedExtras.getInt(Intent.EXTRA_REPLACING))
+                .isEqualTo(expectedExtras.getInt(Intent.EXTRA_REPLACING));
+
         verify(spyPackageMonitor, times(1)).onPackageAppeared(eq(FAKE_PACKAGE_NAME),
                 eq(PackageMonitor.PACKAGE_PERMANENT_CHANGE));
         verify(spyPackageMonitor, times(1)).onSomePackagesChanged();
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 3956241..96c257b 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -16,6 +16,7 @@
 
 package android.security.keystore;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,7 +35,10 @@
 import java.security.Signature;
 import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
@@ -300,6 +304,7 @@
     private final Date mKeyValidityForConsumptionEnd;
     private final @KeyProperties.PurposeEnum int mPurposes;
     private final @KeyProperties.DigestEnum String[] mDigests;
+    private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
     private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
     private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -345,6 +350,7 @@
             Date keyValidityForConsumptionEnd,
             @KeyProperties.PurposeEnum int purposes,
             @KeyProperties.DigestEnum String[] digests,
+            @KeyProperties.DigestEnum Set<String> mgf1Digests,
             @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
             @KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
             @KeyProperties.BlockModeEnum String[] blockModes,
@@ -404,6 +410,9 @@
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
         mPurposes = purposes;
         mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+        // No need to copy the input parameter because the Builder class passes in an immutable
+        // collection.
+        mMgf1Digests = mgf1Digests != null ? mgf1Digests : Collections.emptySet();
         mEncryptionPaddings =
                 ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
         mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
@@ -566,7 +575,7 @@
 
     /**
      * Returns the set of digest algorithms (e.g., {@code SHA-256}, {@code SHA-384} with which the
-     * key can be used or {@code null} if not specified.
+     * key can be used.
      *
      * <p>See {@link KeyProperties}.{@code DIGEST} constants.
      *
@@ -594,6 +603,40 @@
     }
 
     /**
+     * Returns the set of digests that can be used by the MGF1 mask generation function
+     * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+     * scheme.
+     * If not explicitly specified during key generation, the default {@code SHA-1} digest is
+     * used and may be specified when using the key.
+     *
+     * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+     *
+     * @throws IllegalStateException if this set has not been specified.
+     *
+     * @see #isMgf1DigestsSpecified()
+     */
+    @NonNull
+    @FlaggedApi("MGF1_DIGEST_SETTER")
+    public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+        if (mMgf1Digests.isEmpty()) {
+            throw new IllegalStateException("Mask generation function (MGF) not specified");
+        }
+        return new HashSet(mMgf1Digests);
+    }
+
+    /**
+     * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+     * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+     *
+     * @see #getMgf1Digests()
+     */
+    @NonNull
+    @FlaggedApi("MGF1_DIGEST_SETTER")
+    public boolean isMgf1DigestsSpecified() {
+        return !mMgf1Digests.isEmpty();
+    }
+
+    /**
      * Returns the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OEAPPadding},
      * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
      * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -913,6 +956,8 @@
         private Date mKeyValidityForOriginationEnd;
         private Date mKeyValidityForConsumptionEnd;
         private @KeyProperties.DigestEnum String[] mDigests;
+        private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+                Collections.emptySet();
         private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
         private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
@@ -983,6 +1028,9 @@
             if (sourceSpec.isDigestsSpecified()) {
                 mDigests = sourceSpec.getDigests();
             }
+            if (sourceSpec.isMgf1DigestsSpecified()) {
+                mMgf1Digests = sourceSpec.getMgf1Digests();
+            }
             mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
             mSignaturePaddings = sourceSpec.getSignaturePaddings();
             mBlockModes = sourceSpec.getBlockModes();
@@ -1230,6 +1278,30 @@
         }
 
         /**
+         * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+         * used by the mask generation function MGF1 (which is used for certain operations with
+         * the key). Attempts to use the key with any other digest for the mask generation
+         * function will be rejected.
+         *
+         * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+         * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+         * function (MGF1) with a digest.
+         * The default digest for MGF1 is {@code SHA-1}, which will be specified during key creation
+         * time if no digests have been explicitly provided.
+         * When using the key, the caller may not specify any digests that were not provided during
+         * key creation time. The caller may specify the default digest, {@code SHA-1}, if no
+         * digests were explicitly provided during key creation (but it is not necessary to do so).
+         *
+         * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+         */
+        @NonNull
+        @FlaggedApi("MGF1_DIGEST_SETTER")
+        public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+            mMgf1Digests = Set.of(mgf1Digests);
+            return this;
+        }
+
+        /**
          * Sets the set of padding schemes (e.g., {@code PKCS7Padding}, {@code OAEPPadding},
          * {@code PKCS1Padding}, {@code NoPadding}) with which the key can be used when
          * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
@@ -1782,6 +1854,7 @@
                     mKeyValidityForConsumptionEnd,
                     mPurposes,
                     mDigests,
+                    mMgf1Digests,
                     mEncryptionPaddings,
                     mSignaturePaddings,
                     mBlockModes,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 5ab21bc..c1e3bab 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -16,6 +16,7 @@
 
 package android.security.keystore;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,7 +31,10 @@
 import java.security.KeyStore.ProtectionParameter;
 import java.security.Signature;
 import java.security.cert.Certificate;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
@@ -223,6 +227,7 @@
     private final @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
     private final @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
     private final @KeyProperties.DigestEnum String[] mDigests;
+    private final @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests;
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
@@ -247,6 +252,7 @@
             @KeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
             @KeyProperties.SignaturePaddingEnum String[] signaturePaddings,
             @KeyProperties.DigestEnum String[] digests,
+            @KeyProperties.DigestEnum Set<String> mgf1Digests,
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
@@ -271,6 +277,7 @@
         mSignaturePaddings =
                 ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(signaturePaddings));
         mDigests = ArrayUtils.cloneIfNotEmpty(digests);
+        mMgf1Digests = mgf1Digests;
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
@@ -381,6 +388,40 @@
     }
 
     /**
+     * Returns the set of digests that can be used by the MGF1 mask generation function
+     * (e.g., {@code SHA-256}, {@code SHA-384}) with the key. Useful with the {@code RSA-OAEP}
+     * scheme.
+     * If not explicitly specified  during key generation, the default {@code SHA-1} digest is
+     * used and may be specified.
+     *
+     * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+     *
+     * @throws IllegalStateException if this set has not been specified.
+     *
+     * @see #isMgf1DigestsSpecified()
+     */
+    @NonNull
+    @FlaggedApi("MGF1_DIGEST_SETTER")
+    public @KeyProperties.DigestEnum Set<String> getMgf1Digests() {
+        if (mMgf1Digests.isEmpty()) {
+            throw new IllegalStateException("Mask generation function (MGF) not specified");
+        }
+        return new HashSet(mMgf1Digests);
+    }
+
+    /**
+     * Returns {@code true} if the set of digests for the MGF1 mask generation function,
+     * with which the key can be used, has been specified. Useful with the {@code RSA-OAEP} scheme.
+     *
+     * @see #getMgf1Digests()
+     */
+    @NonNull
+    @FlaggedApi("MGF1_DIGEST_SETTER")
+    public boolean isMgf1DigestsSpecified() {
+        return !mMgf1Digests.isEmpty();
+    }
+
+    /**
      * Gets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be used
      * when encrypting/decrypting. Attempts to use the key with any other block modes will be
      * rejected.
@@ -588,6 +629,8 @@
         private @KeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
         private @KeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
         private @KeyProperties.DigestEnum String[] mDigests;
+        private @NonNull @KeyProperties.DigestEnum Set<String> mMgf1Digests =
+                Collections.emptySet();
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
@@ -739,6 +782,30 @@
         }
 
         /**
+         * Sets the set of hash functions (e.g., {@code SHA-256}, {@code SHA-384}) which could be
+         * used by the mask generation function MGF1 (which is used for certain operations with
+         * the key). Attempts to use the key with any other digest for the mask generation
+         * function will be rejected.
+         *
+         * <p>This can only be specified for signing/verification keys and RSA encryption/decryption
+         * keys used with RSA OAEP padding scheme because these operations involve a mask generation
+         * function (MGF1) with a digest.
+         * The default digest for MGF1 is {@code SHA-1}, which will be specified during key import
+         * time if no digests have been explicitly provided.
+         * When using the key, the caller may not specify any digests that were not provided during
+         * key import time. The caller may specify the default digest, {@code SHA-1}, if no
+         * digests were explicitly provided during key import (but it is not necessary to do so).
+         *
+         * <p>See {@link KeyProperties}.{@code DIGEST} constants.
+         */
+        @NonNull
+        @FlaggedApi("MGF1_DIGEST_SETTER")
+        public Builder setMgf1Digests(@Nullable @KeyProperties.DigestEnum String... mgf1Digests) {
+            mMgf1Digests = Set.of(mgf1Digests);
+            return this;
+        }
+
+        /**
          * Sets the set of block modes (e.g., {@code GCM}, {@code CBC}) with which the key can be
          * used when encrypting/decrypting. Attempts to use the key with any other block modes will
          * be rejected.
@@ -1141,6 +1208,7 @@
                     mEncryptionPaddings,
                     mSignaturePaddings,
                     mDigests,
+                    mMgf1Digests,
                     mBlockModes,
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 9356eb8..ceba04e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -23,7 +23,11 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
+import java.util.List;
+import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -91,6 +95,11 @@
         } else {
             out.writeStringArray(null);
         }
+        if (mSpec.isMgf1DigestsSpecified()) {
+            out.writeStringList(List.copyOf(mSpec.getMgf1Digests()));
+        } else {
+            out.writeStringList(null);
+        }
         out.writeStringArray(mSpec.getEncryptionPaddings());
         out.writeStringArray(mSpec.getSignaturePaddings());
         out.writeStringArray(mSpec.getBlockModes());
@@ -153,6 +162,7 @@
         final Date keyValidityForOriginationEnd = readDateOrNull(in);
         final Date keyValidityForConsumptionEnd = readDateOrNull(in);
         final String[] digests = in.createStringArray();
+        final ArrayList<String> mgf1Digests = in.createStringArrayList();
         final String[] encryptionPaddings = in.createStringArray();
         final String[] signaturePaddings = in.createStringArray();
         final String[] blockModes = in.createStringArray();
@@ -191,6 +201,7 @@
                 keyValidityForConsumptionEnd,
                 purposes,
                 digests,
+                mgf1Digests != null ? Set.copyOf(mgf1Digests) : Collections.emptySet(),
                 encryptionPaddings,
                 signaturePaddings,
                 blockModes,
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index 9ac0f6d..101a10e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -24,6 +24,7 @@
 import android.security.KeyStoreException;
 import android.security.KeyStoreOperation;
 import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyStoreCryptoOperation;
 import android.system.keystore2.Authorization;
 
@@ -71,7 +72,7 @@
  */
 abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
     private static final String TAG = "AndroidKeyStoreCipherSpiBase";
-    public static final String DEFAULT_MGF1_DIGEST = "SHA-1";
+    public static final String DEFAULT_MGF1_DIGEST = KeyProperties.DIGEST_SHA1;
 
     // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
     // doFinal finishes.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 1398da3..ed4b485 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -188,6 +188,7 @@
     private int[] mKeymasterEncryptionPaddings;
     private int[] mKeymasterSignaturePaddings;
     private int[] mKeymasterDigests;
+    private int[] mKeymasterMgf1Digests;
 
     private Long mRSAPublicExponent;
 
@@ -323,6 +324,21 @@
                 } else {
                     mKeymasterDigests = EmptyArray.INT;
                 }
+                if (spec.isMgf1DigestsSpecified()) {
+                    // User-specified digests: Add all of them and do _not_ add the SHA-1
+                    // digest by default (stick to what the user provided).
+                    Set<String> mgfDigests = spec.getMgf1Digests();
+                    mKeymasterMgf1Digests = new int[mgfDigests.size()];
+                    int offset = 0;
+                    for (String digest : mgfDigests) {
+                        mKeymasterMgf1Digests[offset] = KeyProperties.Digest.toKeymaster(digest);
+                        offset++;
+                    }
+                } else {
+                    // No user-specified digests: Add the SHA-1 default.
+                    mKeymasterMgf1Digests = new int[]{
+                            KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)};
+                }
 
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
@@ -544,6 +560,7 @@
         mKeymasterEncryptionPaddings = null;
         mKeymasterSignaturePaddings = null;
         mKeymasterDigests = null;
+        mKeymasterMgf1Digests = null;
         mKeySizeBits = 0;
         mSpec = null;
         mRSAPublicExponent = null;
@@ -831,24 +848,11 @@
                     KeymasterDefs.KM_TAG_PADDING, padding
             ));
             if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
-                final boolean[] hasDefaultMgf1DigestBeenAdded = {false};
-                ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+                ArrayUtils.forEach(mKeymasterMgf1Digests, (mgf1Digest) -> {
                     params.add(KeyStore2ParameterUtils.makeEnum(
-                            KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest
+                            KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mgf1Digest
                     ));
-                    hasDefaultMgf1DigestBeenAdded[0] |=
-                            digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST));
                 });
-                /* Because of default MGF1 digest is SHA-1. It has to be added in Key
-                 * characteristics. Otherwise, crypto operations will fail with Incompatible
-                 * MGF1 digest.
-                 */
-                if (!hasDefaultMgf1DigestBeenAdded[0]) {
-                    params.add(KeyStore2ParameterUtils.makeEnum(
-                            KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
-                            KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
-                    ));
-                }
             }
         });
         ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 273dff1..ddbd93e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -526,25 +526,22 @@
                         padding
                 ));
                 if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
-                    if (spec.isDigestsSpecified()) {
-                        boolean hasDefaultMgf1DigestBeenAdded = false;
-                        for (String digest : spec.getDigests()) {
+                    if (spec.isMgf1DigestsSpecified()) {
+                        for (String mgf1Digest : spec.getMgf1Digests()) {
                             importArgs.add(KeyStore2ParameterUtils.makeEnum(
                                     KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
-                                    KeyProperties.Digest.toKeymaster(digest)
+                                    KeyProperties.Digest.toKeymaster(mgf1Digest)
                             ));
-                            hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST);
                         }
+                    } else {
                         /* Because of default MGF1 digest is SHA-1. It has to be added in Key
                          * characteristics. Otherwise, crypto operations will fail with Incompatible
                          * MGF1 digest.
                          */
-                        if (!hasDefaultMgf1DigestBeenAdded) {
-                            importArgs.add(KeyStore2ParameterUtils.makeEnum(
-                                    KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
-                                    KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
-                            ));
-                        }
+                        importArgs.add(KeyStore2ParameterUtils.makeEnum(
+                                KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+                                KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+                        ));
                     }
                 }
             }
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 2ae61ab..d4e2dbc 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -17,6 +17,7 @@
 package android.security;
 
 import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
@@ -101,6 +102,7 @@
         assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
         assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
         assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+        assertThat(spec.isMgf1DigestsSpecified(), is(false));
         assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
         assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
         assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
@@ -189,4 +191,19 @@
         ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
         assertEquals(parcelSpec.getName(), ecSpec.getName());
     }
+
+    @Test
+    public void testParcelingMgf1Digests() {
+        String[] mgf1Digests =
+                new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+                new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                        .setMgf1Digests(mgf1Digests)
+                        .build());
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+                ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        assertArrayEquals(fromParcel.getMgf1Digests().toArray(), mgf1Digests);
+    }
 }
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
index ddbb1d8..da5e8bf 100644
--- a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -16,9 +16,12 @@
 
 package android.security.keystore;
 
+import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
 
 import android.security.ParcelableKeyGenParameterSpecTest;
 
@@ -61,4 +64,54 @@
 
         assertEquals(copiedSpec.getAttestationChallenge(), null);
     }
+
+    @Test
+    public void testMgf1DigestsNotSpecifiedByDefault() {
+        KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+        assertThat(spec.isMgf1DigestsSpecified(), is(false));
+        assertThrows(IllegalStateException.class, () -> {
+            spec.getMgf1Digests();
+        });
+    }
+
+    @Test
+    public void testMgf1DigestsCanBeSpecified() {
+        String[] mgf1Digests =
+                new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+        KeyGenParameterSpec spec =  new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setMgf1Digests(mgf1Digests)
+                .build();
+        assertThat(spec.isMgf1DigestsSpecified(), is(true));
+        assertThat(spec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+
+        KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build();
+        assertThat(copiedSpec.isMgf1DigestsSpecified(), is(true));
+        assertThat(copiedSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+    }
+
+    @Test
+    public void testMgf1DigestsAreNotModified() {
+        String[] mgf1Digests =
+                new String[] {KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256};
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setMgf1Digests(mgf1Digests);
+
+        KeyGenParameterSpec firstSpec =  builder.build();
+        assertArrayEquals(mgf1Digests, firstSpec.getMgf1Digests().toArray());
+
+        String[] otherDigests = new String[] {KeyProperties.DIGEST_SHA224};
+        KeyGenParameterSpec secondSpec =  builder.setMgf1Digests(otherDigests).build();
+        assertThat(secondSpec.getMgf1Digests(), containsInAnyOrder(otherDigests));
+
+        // Now check that the first spec created hasn't changed.
+        assertThat(firstSpec.getMgf1Digests(), containsInAnyOrder(mgf1Digests));
+    }
+
+    @Test
+    public void testEmptyMgf1DigestsCanBeSet() {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setMgf1Digests(new String[] {}).build();
+
+        assertThat(spec.isMgf1DigestsSpecified(), is(false));
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
index 5eeb3b6..b141beb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsLayout.java
@@ -18,12 +18,16 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -36,14 +40,29 @@
  */
 public class UserAspectRatioSettingsLayout extends LinearLayout {
 
+    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+
+    private static final Interpolator PATH_INTERPOLATOR =
+            new PathInterpolator(0.2f, 0f, 0f, 1f);
+
     private static final float ALPHA_FULL_TRANSPARENT = 0f;
 
     private static final float ALPHA_FULL_OPAQUE = 1f;
 
-    private static final long VISIBILITY_ANIMATION_DURATION_MS = 50;
+    private static final float SCALE_START = 0.8f;
+
+    private static final float SCALE_END = 1f;
+
+    private static final long FADE_ANIMATION_DURATION_MS = 167;
+
+    private static final long SCALE_ANIMATION_DURATION_MS = 300;
 
     private static final String ALPHA_PROPERTY_NAME = "alpha";
 
+    private static final String SCALE_X_PROPERTY_NAME = "scaleX";
+
+    private static final String SCALE_Y_PROPERTY_NAME = "scaleY";
+
     private UserAspectRatioSettingsWindowManager mWindowManager;
 
     public UserAspectRatioSettingsLayout(Context context) {
@@ -88,7 +107,7 @@
         if (show) {
             showItem(view);
         } else {
-            view.setVisibility(visibility);
+            hideItem(view);
         }
     }
 
@@ -121,16 +140,40 @@
     }
 
     private void showItem(@NonNull View view) {
-        view.setVisibility(View.VISIBLE);
+        final AnimatorSet animatorSet = new AnimatorSet();
         final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
                 ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
-        fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
-        fadeIn.addListener(new AnimatorListenerAdapter() {
+        fadeIn.setDuration(FADE_ANIMATION_DURATION_MS);
+        fadeIn.setInterpolator(LINEAR_INTERPOLATOR);
+        final ObjectAnimator scaleY =
+                ObjectAnimator.ofFloat(view, SCALE_Y_PROPERTY_NAME, SCALE_START, SCALE_END);
+        final ObjectAnimator scaleX =
+                ObjectAnimator.ofFloat(view, SCALE_X_PROPERTY_NAME, SCALE_START, SCALE_END);
+        scaleX.setDuration(SCALE_ANIMATION_DURATION_MS);
+        scaleX.setInterpolator(PATH_INTERPOLATOR);
+        scaleY.setDuration(SCALE_ANIMATION_DURATION_MS);
+        scaleY.setInterpolator(PATH_INTERPOLATOR);
+        animatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
-            public void onAnimationEnd(Animator animation) {
+            public void onAnimationStart(Animator animation) {
                 view.setVisibility(View.VISIBLE);
             }
         });
-        fadeIn.start();
+        animatorSet.playTogether(fadeIn, scaleY, scaleX);
+        animatorSet.start();
+    }
+
+    private void hideItem(@NonNull View view) {
+        final ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
+                ALPHA_FULL_OPAQUE, ALPHA_FULL_TRANSPARENT);
+        fadeOut.setDuration(FADE_ANIMATION_DURATION_MS);
+        fadeOut.setInterpolator(LINEAR_INTERPOLATOR);
+        fadeOut.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                view.setVisibility(View.GONE);
+            }
+        });
+        fadeOut.start();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f8dd208..09ba4f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -37,6 +37,7 @@
 import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.RemoteTransition
 import android.window.TransitionInfo
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
@@ -65,7 +66,9 @@
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.sysui.ShellSharedConstants
+import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.android.wm.shell.util.KtProtoLog
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -139,20 +142,22 @@
     }
 
     /** Show all tasks, that are part of the desktop, on top of launcher */
-    fun showDesktopApps(displayId: Int) {
+    fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition? = null) {
         KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: showDesktopApps")
         val wct = WindowContainerTransaction()
-        // TODO(b/278084491): pass in display id
         bringDesktopAppsToFront(displayId, wct)
 
-        // Execute transaction if there are pending operations
-        if (!wct.isEmpty) {
-            if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-                // TODO(b/268662477): add animation for the transition
-                transitions.startTransition(TRANSIT_NONE, wct, null /* handler */)
-            } else {
-                shellTaskOrganizer.applyTransaction(wct)
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            // TODO(b/255649902): ensure remote transition is supplied once state is introduced
+            val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
+            val handler = remoteTransition?.let {
+                OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
             }
+            transitions.startTransition(transitionType, wct, handler).also { t ->
+                handler?.setTransition(t)
+            }
+        } else {
+            shellTaskOrganizer.applyTransaction(wct)
         }
     }
 
@@ -1093,11 +1098,11 @@
             controller = null
         }
 
-        override fun showDesktopApps(displayId: Int) {
+        override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
             ExecutorUtils.executeRemoteCallWithTaskPermission(
                 controller,
                 "showDesktopApps"
-            ) { c -> c.showDesktopApps(displayId) }
+            ) { c -> c.showDesktopApps(displayId, remoteTransition) }
         }
 
         override fun stashDesktopApps(displayId: Int) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 47edfd4..6bdaf1e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -17,6 +17,7 @@
 package com.android.wm.shell.desktopmode;
 
 import android.app.ActivityManager.RunningTaskInfo;
+import android.window.RemoteTransition;
 import com.android.wm.shell.desktopmode.IDesktopTaskListener;
 
 /**
@@ -25,7 +26,7 @@
 interface IDesktopMode {
 
     /** Show apps on the desktop on the given display */
-    void showDesktopApps(int displayId);
+    void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
 
     /** Stash apps on the desktop to allow launching another app from home screen */
     void stashDesktopApps(int displayId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index 0d77a2e..ef8393c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -29,12 +29,16 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewTreeObserver;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.concurrent.Executor;
 
 /**
@@ -74,6 +78,7 @@
     private final TaskViewTaskController mTaskViewTaskController;
     private Region mObscuredTouchRegion;
     private Insets mCaptionInsets;
+    private Handler mHandler;
 
     public TaskView(Context context, TaskViewTaskController taskViewTaskController) {
         super(context, null, 0, 0, true /* disableBackgroundLayer */);
@@ -81,6 +86,7 @@
         // TODO(b/266736992): Think about a better way to set the TaskViewBase on the
         //  TaskViewTaskController and vice-versa
         mTaskViewTaskController.setTaskViewBase(this);
+        mHandler = Handler.getMain();
         getHolder().addCallback(this);
     }
 
@@ -117,14 +123,16 @@
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
         onLocationChanged();
         if (taskInfo.taskDescription != null) {
-            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+            final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+            runOnViewThread(() -> setResizeBackgroundColor(bgColor));
         }
     }
 
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
         if (taskInfo.taskDescription != null) {
-            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+            final int bgColor = taskInfo.taskDescription.getBackgroundColor();
+            runOnViewThread(() -> setResizeBackgroundColor(bgColor));
         }
     }
 
@@ -143,7 +151,7 @@
 
     @Override
     public void setResizeBgColor(SurfaceControl.Transaction t, int bgColor) {
-        setResizeBackgroundColor(t, bgColor);
+        runOnViewThread(() -> setResizeBackgroundColor(t, bgColor));
     }
 
     /**
@@ -272,12 +280,14 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+        mHandler = getHandler();
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        mHandler = Handler.getMain();
     }
 
     /** Returns the task info for the task in the TaskView. */
@@ -285,4 +295,24 @@
     public ActivityManager.RunningTaskInfo getTaskInfo() {
         return mTaskViewTaskController.getTaskInfo();
     }
+
+    /**
+     * Sets the handler, only for testing.
+     */
+    @VisibleForTesting
+    void setHandler(Handler viewHandler) {
+        mHandler = viewHandler;
+    }
+
+    /**
+     * Ensures that the given runnable runs on the view's thread.
+     */
+    private void runOnViewThread(Runnable r) {
+        if (mHandler.getLooper().isCurrentThread()) {
+            r.run();
+        } else {
+            // If this call is not from the same thread as the view, then post it
+            mHandler.post(r);
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
new file mode 100644
index 0000000..0df42b3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRemoteTransition.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.wm.shell;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.SurfaceControl;
+import android.window.IRemoteTransition;
+import android.window.IRemoteTransitionFinishedCallback;
+import android.window.TransitionInfo;
+import android.window.WindowContainerTransaction;
+
+/**
+ * {@link IRemoteTransition} for testing purposes.
+ * Stores info about
+ * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+ * IRemoteTransitionFinishedCallback)} being called.
+ */
+public class TestRemoteTransition extends IRemoteTransition.Stub {
+    private boolean mCalled = false;
+    final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
+
+    @Override
+    public void startAnimation(IBinder transition, TransitionInfo info,
+            SurfaceControl.Transaction startTransaction,
+            IRemoteTransitionFinishedCallback finishCallback)
+            throws RemoteException {
+        mCalled = true;
+        finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
+    }
+
+    @Override
+    public void mergeAnimation(IBinder transition, TransitionInfo info,
+            SurfaceControl.Transaction t, IBinder mergeTarget,
+            IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+    }
+
+    /**
+     * Check whether this remote transition
+     * {@link #startAnimation(IBinder, TransitionInfo, SurfaceControl.Transaction,
+     * IRemoteTransitionFinishedCallback)} is called
+     */
+    public boolean isCalled() {
+        return mCalled;
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index c6cccc0..664fbb2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -28,10 +28,10 @@
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.WindowManager
 import android.view.WindowManager.TRANSIT_CHANGE
-import android.view.WindowManager.TRANSIT_NONE
 import android.view.WindowManager.TRANSIT_OPEN
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.DisplayAreaInfo
+import android.window.RemoteTransition
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
@@ -43,6 +43,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRemoteTransition
 import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.TestShellExecutor
 import com.android.wm.shell.common.DisplayController
@@ -57,8 +58,10 @@
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isA
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
 import org.mockito.Mockito
@@ -174,9 +178,9 @@
         markTaskHidden(task1)
         markTaskHidden(task2)
 
-        controller.showDesktopApps(DEFAULT_DISPLAY)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
-        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+        val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -192,9 +196,9 @@
         markTaskVisible(task1)
         markTaskVisible(task2)
 
-        controller.showDesktopApps(DEFAULT_DISPLAY)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
-        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+        val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -210,9 +214,9 @@
         markTaskHidden(task1)
         markTaskVisible(task2)
 
-        controller.showDesktopApps(DEFAULT_DISPLAY)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
-        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+        val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
         assertThat(wct.hierarchyOps).hasSize(3)
         // Expect order to be from bottom: home, task1, task2
         wct.assertReorderAt(index = 0, homeTask)
@@ -224,9 +228,9 @@
     fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
         val homeTask = setUpHomeTask()
 
-        controller.showDesktopApps(DEFAULT_DISPLAY)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
-        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+        val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
         assertThat(wct.hierarchyOps).hasSize(1)
         wct.assertReorderAt(index = 0, homeTask)
     }
@@ -240,9 +244,9 @@
         markTaskHidden(taskDefaultDisplay)
         markTaskHidden(taskSecondDisplay)
 
-        controller.showDesktopApps(DEFAULT_DISPLAY)
+        controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
 
-        val wct = getLatestWct(expectTransition = TRANSIT_NONE)
+        val wct = getLatestWct(type = TRANSIT_OPEN, handlerClass = OneShotRemoteHandler::class.java)
         assertThat(wct.hierarchyOps).hasSize(2)
         // Expect order to be from bottom: home, task
         wct.assertReorderAt(index = 0, homeTaskDefaultDisplay)
@@ -373,7 +377,7 @@
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN
         controller.moveToFullscreen(task)
-        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+        val wct = getLatestWct(type = TRANSIT_CHANGE)
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
@@ -383,7 +387,7 @@
         val task = setUpFreeformTask()
         task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM
         controller.moveToFullscreen(task)
-        val wct = getLatestWct(expectTransition = TRANSIT_CHANGE)
+        val wct = getLatestWct(type = TRANSIT_CHANGE)
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
                 .isEqualTo(WINDOWING_MODE_FULLSCREEN)
     }
@@ -401,7 +405,7 @@
 
         controller.moveToFullscreen(taskDefaultDisplay)
 
-        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
             assertThat(changes.keys).contains(taskDefaultDisplay.token.asBinder())
             assertThat(changes.keys).doesNotContain(taskSecondDisplay.token.asBinder())
         }
@@ -414,7 +418,7 @@
 
         controller.moveTaskToFront(task1)
 
-        val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT)
+        val wct = getLatestWct(type = TRANSIT_TO_FRONT)
         assertThat(wct.hierarchyOps).hasSize(1)
         wct.assertReorderAt(index = 0, task1)
     }
@@ -439,7 +443,7 @@
 
         val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
         controller.moveToNextDisplay(task.taskId)
-        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
             assertThat(hierarchyOps).hasSize(1)
             assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
             assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -461,7 +465,7 @@
         val task = setUpFreeformTask(displayId = SECOND_DISPLAY)
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(expectTransition = TRANSIT_CHANGE)) {
+        with(getLatestWct(type = TRANSIT_CHANGE)) {
             assertThat(hierarchyOps).hasSize(1)
             assertThat(hierarchyOps[0].container).isEqualTo(task.token.asBinder())
             assertThat(hierarchyOps[0].isReparent).isTrue()
@@ -747,11 +751,16 @@
     }
 
     private fun getLatestWct(
-        @WindowManager.TransitionType expectTransition: Int = TRANSIT_OPEN
+            @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
+            handlerClass: Class<out TransitionHandler>? = null
     ): WindowContainerTransaction {
         val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
         if (ENABLE_SHELL_TRANSITIONS) {
-            verify(transitions).startTransition(eq(expectTransition), arg.capture(), isNull())
+            if (handlerClass == null) {
+                verify(transitions).startTransition(eq(type), arg.capture(), isNull())
+            } else {
+                verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
+            }
         } else {
             verify(shellTaskOrganizer).applyTransaction(arg.capture())
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 5efd9ad..d542139 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -47,11 +47,8 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
-import android.window.IRemoteTransition;
-import android.window.IRemoteTransitionFinishedCallback;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -65,6 +62,7 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRemoteTransition;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.TransitionInfoBuilder;
 import com.android.wm.shell.common.DisplayController;
@@ -205,7 +203,7 @@
 
         // Make sure split-screen is now visible
         assertTrue(mStageCoordinator.isSplitScreenVisible());
-        assertTrue(testRemote.mCalled);
+        assertTrue(testRemote.isCalled());
     }
 
     @Test
@@ -468,24 +466,4 @@
         return out;
     }
 
-    class TestRemoteTransition extends IRemoteTransition.Stub {
-        boolean mCalled = false;
-        final WindowContainerTransaction mRemoteFinishWCT = new WindowContainerTransaction();
-
-        @Override
-        public void startAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction startTransaction,
-                IRemoteTransitionFinishedCallback finishCallback)
-                throws RemoteException {
-            mCalled = true;
-            finishCallback.onTransitionFinished(mRemoteFinishWCT, null /* sct */);
-        }
-
-        @Override
-        public void mergeAnimation(IBinder transition, TransitionInfo info,
-                SurfaceControl.Transaction t, IBinder mergeTarget,
-                IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
-        }
-    }
-
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0088051..4afb29e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -43,6 +43,8 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Handler;
+import android.os.Looper;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.SurfaceControl;
@@ -88,6 +90,10 @@
     SyncTransactionQueue mSyncQueue;
     @Mock
     Transitions mTransitions;
+    @Mock
+    Looper mViewLooper;
+    @Mock
+    Handler mViewHandler;
 
     SurfaceSession mSession;
     SurfaceControl mLeash;
@@ -105,6 +111,8 @@
                 .build();
 
         mContext = getContext();
+        doReturn(true).when(mViewLooper).isCurrentThread();
+        doReturn(mViewLooper).when(mViewHandler).getLooper();
 
         mTaskInfo = new ActivityManager.RunningTaskInfo();
         mTaskInfo.token = mToken;
@@ -132,6 +140,7 @@
         mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer,
                 mTaskViewTransitions, mSyncQueue));
         mTaskView = new TaskView(mContext, mTaskViewTaskController);
+        mTaskView.setHandler(mViewHandler);
         mTaskView.setListener(mExecutor, mViewListener);
     }
 
@@ -646,4 +655,17 @@
 
         assertThat(mTaskViewTaskController.getTaskInfo()).isNull();
     }
+
+    @Test
+    public void testOnTaskInfoChangedOnSameUiThread() {
+        mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+        verify(mViewHandler, never()).post(any());
+    }
+
+    @Test
+    public void testOnTaskInfoChangedOnDifferentUiThread() {
+        doReturn(false).when(mViewLooper).isCurrentThread();
+        mTaskViewTaskController.onTaskInfoChanged(mTaskInfo);
+        verify(mViewHandler).post(any());
+    }
 }
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 764d1ef..69fda34 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -166,11 +166,12 @@
 #endif
                 mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
                                             ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
-                                            mBounds)
+                                            SkSpan<SkRuntimeEffect::ChildPtr>(), mBounds)
                                 .mesh;
             } else {
                 mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
-                                     mBuilder->fUniforms, mBounds)
+                                     mBuilder->fUniforms, SkSpan<SkRuntimeEffect::ChildPtr>(),
+                                     mBounds)
                                 .mesh;
             }
             mIsDirty = false;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 71f47e9..ff0d8d7 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -539,7 +539,7 @@
             if (!cpuMesh.indexBuffer()) {
                 gpuMesh = SkMesh::Make(cpuMesh.refSpec(), cpuMesh.mode(), vb, cpuMesh.vertexCount(),
                                        cpuMesh.vertexOffset(), cpuMesh.refUniforms(),
-                                       cpuMesh.bounds())
+                                       SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
                                   .mesh;
             } else {
                 sk_sp<SkMesh::IndexBuffer> ib =
@@ -547,7 +547,8 @@
                 gpuMesh = SkMesh::MakeIndexed(cpuMesh.refSpec(), cpuMesh.mode(), vb,
                                               cpuMesh.vertexCount(), cpuMesh.vertexOffset(), ib,
                                               cpuMesh.indexCount(), cpuMesh.indexOffset(),
-                                              cpuMesh.refUniforms(), cpuMesh.bounds())
+                                              cpuMesh.refUniforms(),
+                                              SkSpan<SkRuntimeEffect::ChildPtr>(), cpuMesh.bounds())
                                   .mesh;
             }
 
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index d1ebe6d..1c3399a 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -72,6 +72,7 @@
         mSessionValid = true;
         mHintSession = nullptr;
     }
+    mResetsSinceLastReport = 0;
 }
 
 bool HintSessionWrapper::init() {
@@ -109,12 +110,13 @@
     tids.push_back(mUiThreadId);
     tids.push_back(mRenderThreadId);
 
-    // Use a placeholder target value to initialize,
-    // this will always be replaced elsewhere before it gets used
-    int64_t defaultTargetDurationNanos = 16666667;
+    // Use the cached target value if there is one, otherwise use a default. This is to ensure
+    // the cached target and target in PowerHAL are consistent, and that it updates correctly
+    // whenever there is a change.
+    int64_t targetDurationNanos =
+            mLastTargetWorkDuration == 0 ? kDefaultTargetDuration : mLastTargetWorkDuration;
     mHintSessionFuture = CommonPool::async([=, this, tids = std::move(tids)] {
-        return mBinding->createSession(manager, tids.data(), tids.size(),
-                                       defaultTargetDurationNanos);
+        return mBinding->createSession(manager, tids.data(), tids.size(), targetDurationNanos);
     });
     return false;
 }
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 36e91ea..41891cd 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -65,6 +65,7 @@
     static constexpr nsecs_t kResetHintTimeout = 100_ms;
     static constexpr int64_t kSanityCheckLowerBound = 100_us;
     static constexpr int64_t kSanityCheckUpperBound = 10_s;
+    static constexpr int64_t kDefaultTargetDuration = 16666667;
 
     // Allows easier stub when testing
     class HintSessionBinding {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 4eabfb2..8445032 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -26,6 +26,21 @@
     srcs: [
         "PointerController_test.cpp",
     ],
+    sanitize: {
+        hwaddress: true,
+        undefined: true,
+        all_undefined: true,
+        diag: {
+            undefined: true,
+        },
+    },
+    target: {
+        host: {
+            sanitize: {
+                address: true,
+            },
+        },
+    },
     shared_libs: [
         "libandroid_runtime",
         "libinputservice",
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 94faf4a..d9efd3c 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -148,6 +148,25 @@
     latestPointerDisplayId = displayId;
 }
 
+class TestPointerController : public PointerController {
+public:
+    TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
+                          sp<PointerControllerPolicyInterface> policy, const sp<Looper>& looper,
+                          SpriteController& spriteController)
+          : PointerController(
+                    policy, looper, spriteController,
+                    /*enabled=*/true,
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Register listener
+                        registeredListener = listener;
+                    },
+                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
+                        // Unregister listener
+                        if (registeredListener == listener) registeredListener = nullptr;
+                    }) {}
+    ~TestPointerController() override {}
+};
+
 class PointerControllerTest : public Test {
 protected:
     PointerControllerTest();
@@ -159,6 +178,7 @@
     sp<MockPointerControllerPolicyInterface> mPolicy;
     std::unique_ptr<MockSpriteController> mSpriteController;
     std::shared_ptr<PointerController> mPointerController;
+    sp<android::gui::WindowInfosListener> mRegisteredListener;
 
 private:
     void loopThread();
@@ -181,11 +201,12 @@
     EXPECT_CALL(*mSpriteController, createSprite())
             .WillOnce(Return(mPointerSprite));
 
-    mPointerController =
-            PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
+    mPointerController = std::make_unique<TestPointerController>(mRegisteredListener, mPolicy,
+                                                                 mLooper, *mSpriteController);
 }
 
 PointerControllerTest::~PointerControllerTest() {
+    mPointerController.reset();
     mRunning.store(false, std::memory_order_relaxed);
     mThread.join();
 }
@@ -316,31 +337,16 @@
 
 class PointerControllerWindowInfoListenerTest : public Test {};
 
-class TestPointerController : public PointerController {
-public:
-    TestPointerController(sp<android::gui::WindowInfosListener>& registeredListener,
-                          const sp<Looper>& looper, SpriteController& spriteController)
-          : PointerController(
-                    new MockPointerControllerPolicyInterface(), looper, spriteController,
-                    /*enabled=*/true,
-                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
-                        // Register listener
-                        registeredListener = listener;
-                    },
-                    [&registeredListener](const sp<android::gui::WindowInfosListener>& listener) {
-                        // Unregister listener
-                        if (registeredListener == listener) registeredListener = nullptr;
-                    }) {}
-};
-
 TEST_F(PointerControllerWindowInfoListenerTest,
        doesNotCrashIfListenerCalledAfterPointerControllerDestroyed) {
     sp<Looper> looper = new Looper(false);
     auto spriteController = NiceMock<MockSpriteController>(looper);
     sp<android::gui::WindowInfosListener> registeredListener;
     sp<android::gui::WindowInfosListener> localListenerCopy;
+    sp<MockPointerControllerPolicyInterface> policy = new MockPointerControllerPolicyInterface();
     {
-        TestPointerController pointerController(registeredListener, looper, spriteController);
+        TestPointerController pointerController(registeredListener, policy, looper,
+                                                spriteController);
         ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
         localListenerCopy = registeredListener;
     }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9924fae..09f09b9 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -2409,13 +2409,16 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
     @Nullable
     public Descrambler openDescrambler() {
+        acquireTRMSLock("openDescrambler()");
         mDemuxLock.lock();
         try {
-            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, mDemuxLock)) {
+            // no need to unlock mDemuxLock (so pass null instead) as TRMS lock is already acquired
+            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, null)) {
                 return null;
             }
             return requestDescrambler();
         } finally {
+            releaseTRMSLock();
             mDemuxLock.unlock();
         }
     }
diff --git a/mime/Android.bp b/mime/Android.bp
index a3ea65c..757862b 100644
--- a/mime/Android.bp
+++ b/mime/Android.bp
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 package {
     // See: http://go/android-license-faq
     // A large-scale-change added 'default_applicable_licenses' to import
@@ -125,6 +124,6 @@
     srcs: [
         "java-res/vendor.mime.types",
     ],
-    //    strip comments            normalize whitepace       drop empty lines   prepend ? to fields that are missing it
-    cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | grep ' '         | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
+    //    strip comments            normalize whitepace       drop empty lines           prepend ? to fields that are missing it
+    cmd: "awk '{gsub(/#.*$$/,\"\"); $$1=$$1; print;}' $(in) | (grep ' ' || echo -n '') | awk '{for(i=1;i<=NF;i++) { sub(/^\\??/, \"?\", $$i); }; print}' > $(out)",
 }
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 38d98a9..0d4af2a 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -12,6 +12,7 @@
     manifest: "AndroidManifest.xml",
     srcs: ["src/**/*.kt"],
     static_libs: [
+        "androidx.activity_activity-compose",
         "androidx.core_core-ktx",
         "androidx.credentials_credentials",
         "guava",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
new file mode 100644
index 0000000..6498ff7
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager
+
+const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index defba8d..8986e52 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -17,14 +17,25 @@
 package com.android.credentialmanager
 
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.credentials.ui.RequestInfo
 import com.android.credentialmanager.ktx.requestInfo
 import com.android.credentialmanager.mapper.toGet
 import com.android.credentialmanager.mapper.toRequestCancel
+import com.android.credentialmanager.mapper.toRequestClose
 import com.android.credentialmanager.model.Request
 
-fun Intent.parse(): Request {
-    this.toRequestCancel()?.let { return it }
+fun Intent.parse(
+    packageManager: PackageManager,
+    previousIntent: Intent? = null,
+): Request {
+    this.toRequestClose(packageManager, previousIntent)?.let { closeRequest ->
+        return closeRequest
+    }
+
+    this.toRequestCancel(packageManager)?.let { cancelRequest ->
+        return cancelRequest
+    }
 
     return when (requestInfo?.type) {
         RequestInfo.TYPE_CREATE -> {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
new file mode 100644
index 0000000..ef083fd
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.credentialmanager.activity
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts
+
+/**
+ * A custom StartIntentSenderForResult contract implementation that attaches an [ActivityOptions]
+ * that opts in for background activity launch.
+ */
+class StartBalIntentSenderForResultContract :
+    ActivityResultContract<IntentSenderRequest, ActivityResult>() {
+    override fun createIntent(context: Context, input: IntentSenderRequest): Intent {
+        val activityOptionBundle =
+            ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+            ).toBundle()
+        return Intent(
+            ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST
+        ).putExtra(
+            ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE,
+            activityOptionBundle
+        ).putExtra(
+            ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST,
+            input
+        )
+    }
+
+    override fun parseResult(
+        resultCode: Int,
+        intent: Intent?
+    ): ActivityResult = ActivityResult(resultCode, intent)
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index a4c20bf..4533db6 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -18,10 +18,12 @@
 
 import android.content.Intent
 import android.credentials.ui.CancelUiRequest
+import android.credentials.ui.Constants
 import android.credentials.ui.CreateCredentialProviderData
 import android.credentials.ui.GetCredentialProviderData
 import android.credentials.ui.ProviderData
 import android.credentials.ui.RequestInfo
+import android.os.ResultReceiver
 
 val Intent.cancelUiRequest: CancelUiRequest?
     get() = this.extras?.getParcelable(
@@ -46,3 +48,9 @@
         ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
         CreateCredentialProviderData::class.java
     ) ?: emptyList()
+
+val Intent.resultReceiver: ResultReceiver?
+    get() = this.getParcelableExtra(
+        Constants.EXTRA_RESULT_RECEIVER,
+        ResultReceiver::class.java
+    )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
index 86a6d23..555a86f 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -17,13 +17,23 @@
 package com.android.credentialmanager.mapper
 
 import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.ktx.appLabel
 import com.android.credentialmanager.ktx.cancelUiRequest
 import com.android.credentialmanager.model.Request
 
-fun Intent.toRequestCancel(): Request.Cancel? =
+fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
     this.cancelUiRequest?.let { cancelUiRequest ->
-        Request.Cancel(
-            showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
-            appPackageName = cancelUiRequest.appPackageName
-        )
+        val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+        if (appLabel == null) {
+            Log.d(TAG, "Received UI cancel request with an invalid package name.")
+            null
+        } else {
+            Request.Cancel(
+                showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
+                appName = appLabel
+            )
+        }
     }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
new file mode 100644
index 0000000..6de3e7d
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.mapper
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.model.Request
+
+fun Intent.toRequestClose(
+    packageManager: PackageManager,
+    previousIntent: Intent? = null,
+): Request.Close? {
+    // Close request comes as "Cancel" request from Credential Manager API
+    val currentRequest = toRequestCancel(packageManager = packageManager) ?: return null
+
+    if (currentRequest.showCancellationUi) {
+        // Current request is to Cancel and not to Close
+        return null
+    }
+
+    previousIntent?.let {
+        val previousToken = previousIntent.requestInfo?.token
+        val currentToken = this.requestInfo?.token
+
+        if (previousToken != currentToken) {
+            // Current cancellation is for a different request, don't close the current flow.
+            return null
+        }
+    }
+
+    return Request.Close
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index ed9d563..ee45fbb 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -1,22 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.mapper
 
 import android.content.Intent
+import android.credentials.ui.Entry
 import android.credentials.ui.GetCredentialProviderData
+import androidx.credentials.provider.PasswordCredentialEntry
+import com.android.credentialmanager.factory.fromSlice
 import com.android.credentialmanager.ktx.getCredentialProviderDataList
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.ktx.resultReceiver
+import com.android.credentialmanager.model.Password
 import com.android.credentialmanager.model.Request
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableMap
 
-fun Intent.toGet() = Request.Get(
-    providers = ImmutableMap.copyOf(
-        getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
-    ),
-    entries = ImmutableList.copyOf(
-        getCredentialProviderDataList.map { providerData ->
-            check(providerData is GetCredentialProviderData) {
-                "Invalid provider data type for GetCredentialRequest"
+fun Intent.toGet(): Request.Get {
+    val credentialEntries = mutableListOf<Pair<String, Entry>>()
+    for (providerData in getCredentialProviderDataList) {
+        if (providerData is GetCredentialProviderData) {
+            for (credentialEntry in providerData.credentialEntries) {
+                credentialEntries.add(
+                    Pair(providerData.providerFlattenedComponentName, credentialEntry)
+                )
             }
-            providerData
-        }.flatMap { it.credentialEntries }
+        }
+    }
+
+    val passwordEntries = mutableListOf<Password>()
+    for ((providerId, entry) in credentialEntries) {
+        val slice = fromSlice(entry.slice)
+        if (slice is PasswordCredentialEntry) {
+            passwordEntries.add(
+                Password(
+                    providerId = providerId,
+                    entry = entry,
+                    passwordCredentialEntry = slice
+                )
+            )
+        }
+    }
+
+    return Request.Get(
+        token = requestInfo?.token,
+        resultReceiver = this.resultReceiver,
+        providers = ImmutableMap.copyOf(
+            getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
+        ),
+        passwordEntries = ImmutableList.copyOf(passwordEntries)
     )
-)
\ No newline at end of file
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
new file mode 100644
index 0000000..2fe4fd5
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.model
+
+import android.credentials.ui.Entry
+import androidx.credentials.provider.PasswordCredentialEntry
+
+data class Password(
+    val providerId: String,
+    val entry: Entry,
+    val passwordCredentialEntry: PasswordCredentialEntry,
+)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index bc07310..6011a1c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,8 +16,9 @@
 
 package com.android.credentialmanager.model
 
-import android.credentials.ui.Entry
 import android.credentials.ui.ProviderData
+import android.os.IBinder
+import android.os.ResultReceiver
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableMap
 
@@ -25,15 +26,33 @@
  * Represents the request made by the CredentialManager API.
  */
 sealed class Request {
+
+    /**
+     * Request to close the app without displaying a message to the user and without reporting
+     * anything back to the Credential Manager service.
+     */
+    data object Close : Request()
+
+    /**
+     * Request to close the app, displaying a message to the user.
+     */
     data class Cancel(
         val showCancellationUi: Boolean,
-        val appPackageName: String?
+        val appName: String
     ) : Request()
 
+    /**
+     * Request to start the get credentials flow.
+     */
     data class Get(
+        val token: IBinder?,
+        val resultReceiver: ResultReceiver?,
         val providers: ImmutableMap<String, ProviderData>,
-        val entries: ImmutableList<Entry>,
+        val passwordEntries: ImmutableList<Password>,
     ) : Request()
 
+    /**
+     * Request to start the create credentials flow.
+     */
     data object Create : Request()
 }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
new file mode 100644
index 0000000..5ab5ab9
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.repository
+
+import android.app.Application
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.parse
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class RequestRepository(
+    private val application: Application,
+) {
+
+    private val _requests = MutableStateFlow<Request?>(null)
+    val requests: StateFlow<Request?> = _requests
+
+    suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
+        val request = intent.parse(
+            packageManager = application.packageManager,
+            previousIntent = previousIntent
+        )
+
+        Log.d(TAG, "Request parsed: $request")
+
+        _requests.value = request
+    }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 943c2b4..ba88484 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,23 +16,183 @@
 
 package com.android.credentialmanager.autofill
 
+import android.app.assist.AssistStructure
+import android.content.Context
+import android.credentials.GetCredentialRequest
+import android.credentials.CredentialManager
+import android.credentials.GetCandidateCredentialsResponse
+import android.credentials.CredentialOption
+import android.credentials.GetCandidateCredentialsException
+import android.os.Bundle
 import android.os.CancellationSignal
-import android.service.autofill.AutofillService
-import android.service.autofill.FillCallback
+import android.os.OutcomeReceiver
 import android.service.autofill.FillRequest
+import android.service.autofill.AutofillService
+import android.service.autofill.FillResponse
+import android.service.autofill.FillCallback
 import android.service.autofill.SaveRequest
 import android.service.autofill.SaveCallback
+import android.util.Log
+import org.json.JSONObject
+import java.util.concurrent.Executors
 
 class CredentialAutofillService : AutofillService() {
+
+    companion object {
+        private const val TAG = "CredAutofill"
+
+        private const val CRED_HINT_PREFIX = "credential="
+        private const val REQUEST_DATA_KEY = "requestData"
+        private const val CANDIDATE_DATA_KEY = "candidateQueryData"
+        private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
+        private const val CRED_OPTIONS_KEY = "credentialOptions"
+        private const val TYPE_KEY = "type"
+    }
+
+    private val credentialManager: CredentialManager =
+            getSystemService(Context.CREDENTIAL_SERVICE) as CredentialManager
+
     override fun onFillRequest(
             request: FillRequest,
             cancellationSignal: CancellationSignal,
             callback: FillCallback
     ) {
+        val context = request.fillContexts
+        val structure = context[context.size - 1].structure
+        val callingPackage = structure.activityComponent.packageName
+        Log.i(TAG, "onFillRequest called for $callingPackage")
+
+        val getCredRequest: GetCredentialRequest? = getCredManRequest(structure)
+        if (getCredRequest == null) {
+            callback.onFailure("No credential manager request found")
+            return
+        }
+
+        val outcome = object : OutcomeReceiver<GetCandidateCredentialsResponse,
+                GetCandidateCredentialsException> {
+            override fun onResult(result: GetCandidateCredentialsResponse) {
+                Log.i(TAG, "getCandidateCredentials onResponse")
+                val fillResponse: FillResponse? = convertToFillResponse(result, request)
+                callback.onSuccess(fillResponse)
+            }
+
+            override fun onError(error: GetCandidateCredentialsException) {
+                Log.i(TAG, "getCandidateCredentials onError")
+                callback.onFailure("error received from credential manager ${error.message}")
+            }
+        }
+
+        credentialManager.getCandidateCredentials(
+                getCredRequest,
+                callingPackage,
+                CancellationSignal(),
+                Executors.newSingleThreadExecutor(),
+                outcome
+        )
+    }
+
+    private fun convertToFillResponse(
+            getCredResponse: GetCandidateCredentialsResponse,
+            filLRequest: FillRequest
+    ): FillResponse? {
         TODO("Not yet implemented")
     }
 
     override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
         TODO("Not yet implemented")
     }
+
+    private fun getCredManRequest(structure: AssistStructure): GetCredentialRequest? {
+        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+        traverseStructure(structure, credentialOptions)
+
+        if (credentialOptions.isNotEmpty()) {
+            return GetCredentialRequest.Builder(Bundle.EMPTY)
+                    .setCredentialOptions(credentialOptions)
+                    .build()
+        }
+        return null
+    }
+
+    private fun traverseStructure(
+            structure: AssistStructure,
+            cmRequests: MutableList<CredentialOption>
+    ) {
+        val windowNodes: List<AssistStructure.WindowNode> =
+                structure.run {
+                    (0 until windowNodeCount).map { getWindowNodeAt(it) }
+                }
+
+        windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
+            traverseNode(windowNode.rootViewNode, cmRequests)
+        }
+    }
+
+    private fun traverseNode(
+            viewNode: AssistStructure.ViewNode?,
+            cmRequests: MutableList<CredentialOption>
+    ) {
+        val options = getCredentialOptionsFromViewNode(viewNode)
+        cmRequests.addAll(options)
+
+        val children: List<AssistStructure.ViewNode>? =
+                viewNode?.run {
+                    (0 until childCount).map { getChildAt(it) }
+                }
+
+        children?.forEach { childNode: AssistStructure.ViewNode ->
+            traverseNode(childNode, cmRequests)
+        }
+    }
+
+    private fun getCredentialOptionsFromViewNode(viewNode: AssistStructure.ViewNode?):
+            List<CredentialOption> {
+        // TODO(b/293945193) Replace with isCredential check from viewNode
+        val credentialHints: MutableList<String> = mutableListOf()
+        if (viewNode != null && viewNode.autofillHints != null) {
+            for (hint in viewNode.autofillHints!!) {
+                if (hint.startsWith(CRED_HINT_PREFIX)) {
+                    credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
+                }
+            }
+        }
+
+        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+        for (credentialHint in credentialHints) {
+            convertJsonToCredentialOption(credentialHint).let { credentialOptions.addAll(it) }
+        }
+        return credentialOptions
+    }
+
+    private fun convertJsonToCredentialOption(jsonString: String): List<CredentialOption> {
+        // TODO(b/302000646) Move this logic to jetpack so that is consistent
+        //  with building the json
+        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
+
+        val json = JSONObject(jsonString)
+        val options = json.getJSONArray(CRED_OPTIONS_KEY)
+        for (i in 0 until options.length()) {
+            val option = options.getJSONObject(i)
+
+            credentialOptions.add(CredentialOption(
+                    option.getString(TYPE_KEY),
+                    convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
+                    convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY)),
+                    option.getBoolean(SYS_PROVIDER_REQ_KEY),
+            ))
+        }
+        return credentialOptions
+    }
+
+    private fun convertJsonToBundle(json: JSONObject): Bundle {
+        val result = Bundle()
+        json.keys().forEach {
+            val v = json.get(it)
+            when (v) {
+                is String -> result.putString(it, v)
+                is Boolean -> result.putBoolean(it, v)
+            }
+        }
+        return result
+    }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c0dff16..e5f5cc2 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -37,6 +37,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.lifecycle_lifecycle-runtime-compose",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
         "androidx.wear.compose_compose-foundation",
         "androidx.wear.compose_compose-material",
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 90248734..b480ac3 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <!--
 /*
  * Copyright (c) 2023 Google Inc.
@@ -21,25 +22,27 @@
 
     <uses-feature android:name="android.hardware.type.watch" />
 
-    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
-    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
-    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
 
     <application
-      android:allowBackup="true"
-      android:dataExtractionRules="@xml/data_extraction_rules"
-      android:fullBackupContent="@xml/backup_rules"
-      android:label="@string/app_name"
-      android:supportsRtl="true">
+        android:name=".CredentialSelectorApp"
+        android:allowBackup="true"
+        android:dataExtractionRules="@xml/data_extraction_rules"
+        android:fullBackupContent="@xml/backup_rules"
+        android:label="@string/app_name"
+        android:supportsRtl="true">
 
+        <!-- Activity called by GMS has to be exactly:
+        com.android.credentialmanager.CredentialSelectorActivity -->
         <activity
-            android:name=".ui.CredentialSelectorActivity"
+            android:name=".CredentialSelectorActivity"
+            android:excludeFromRecents="true"
             android:exported="true"
-            android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
-            android:launchMode="singleTop"
             android:label="@string/app_name"
-            android:excludeFromRecents="true">
-        </activity>
-  </application>
+            android:launchMode="singleTop"
+            android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+    </application>
 
 </manifest>
diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/packages/CredentialManager/wear/res/values/themes.xml
deleted file mode 100644
index 22329e9f..0000000
--- a/packages/CredentialManager/wear/res/values/themes.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 The Android Open 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="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
-    <item name="android:windowContentOverlay">@null</item>
-    <item name="android:windowNoTitle">true</item>
-    <item name="android:windowBackground">@android:color/transparent</item>
-    <item name="android:windowIsTranslucent">true</item>
-  </style>
-</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
new file mode 100644
index 0000000..273d0b1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.ui.WearApp
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import kotlinx.coroutines.launch
+
+class CredentialSelectorActivity : ComponentActivity() {
+
+    private val viewModel: CredentialSelectorViewModel by viewModels {
+        CredentialSelectorViewModel.Factory
+    }
+
+    @OptIn(ExperimentalHorologistApi::class)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setTheme(android.R.style.Theme_DeviceDefault)
+
+        // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
+        // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
+        // contents of the "if" bracket.
+        if (false) {
+            setContent {
+                MaterialTheme {
+                    WearApp(
+                        viewModel = viewModel,
+                        onCloseApp = ::finish,
+                    )
+                }
+            }
+        } else {
+            // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
+            lifecycleScope.launch {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    viewModel.uiState.collect { uiState ->
+                        when (uiState) {
+                            CredentialSelectorUiState.Idle -> {
+                                // Don't display anything, assuming that there should be minimal latency
+                                // to parse the Credential Manager intent and define the state of the
+                                // app. If latency is big, then a "loading" screen should be displayed
+                                // to the user.
+                            }
+
+                            is CredentialSelectorUiState.Get -> {
+                                setContent {
+                                    MaterialTheme {
+                                        SinglePasswordScreen(
+                                            columnState = belowTimeTextPreview(),
+                                            onCloseApp = ::finish,
+                                        )
+                                    }
+                                }
+                            }
+
+                            else -> finish()
+                        }
+                    }
+                }
+            }
+        }
+
+        viewModel.onNewIntent(intent)
+    }
+
+    override fun onNewIntent(intent: Intent) {
+        super.onNewIntent(intent)
+
+        val previousIntent = getIntent()
+        setIntent(intent)
+
+        viewModel.onNewIntent(intent, previousIntent)
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
new file mode 100644
index 0000000..7c81fd0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager
+
+import android.app.Application
+import com.android.credentialmanager.di.inject
+import com.android.credentialmanager.repository.RequestRepository
+
+class CredentialSelectorApp : Application() {
+
+    lateinit var requestRepository: RequestRepository
+
+    override fun onCreate() {
+        super.onCreate()
+
+        inject()
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
new file mode 100644
index 0000000..d557dc0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager
+
+import android.content.Intent
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.mappers.toGet
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class CredentialSelectorViewModel(
+    private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+    val uiState: StateFlow<CredentialSelectorUiState> = requestRepository.requests
+        .map { request ->
+            when (request) {
+                null -> CredentialSelectorUiState.Idle
+                is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
+                Request.Close -> CredentialSelectorUiState.Close
+                Request.Create -> CredentialSelectorUiState.Create
+                is Request.Get -> request.toGet()
+            }
+        }
+        .stateIn(
+            viewModelScope,
+            started = SharingStarted.WhileSubscribed(5000),
+            initialValue = CredentialSelectorUiState.Idle,
+        )
+
+    fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
+        viewModelScope.launch {
+            requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
+        }
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+
+                return CredentialSelectorViewModel(
+                    requestRepository = (application as CredentialSelectorApp).requestRepository,
+                ) as T
+            }
+        }
+    }
+}
+
+sealed class CredentialSelectorUiState {
+    data object Idle : CredentialSelectorUiState()
+    sealed class Get : CredentialSelectorUiState() {
+        data object SingleProviderSinglePasskey : Get()
+        data object SingleProviderSinglePassword : Get()
+
+        // TODO: b/301206470 add the remaining states
+    }
+
+    data object Create : CredentialSelectorUiState()
+    data class Cancel(val appName: String) : CredentialSelectorUiState()
+    data object Close : CredentialSelectorUiState()
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
new file mode 100644
index 0000000..a11017b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
@@ -0,0 +1,17 @@
+package com.android.credentialmanager.di
+
+import android.app.Application
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.repository.RequestRepository
+
+// TODO b/301601582 add Hilt for dependency injection
+
+fun CredentialSelectorApp.inject() {
+    requestRepository = requestRepository(application = this)
+}
+
+private fun requestRepository(
+    application: Application,
+): RequestRepository = RequestRepository(
+    application = application,
+)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
deleted file mode 100644
index 53122ba..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open 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.0N
- *
- * 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.credentialmanager.ui
-
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.wear.compose.material.MaterialTheme
-import kotlinx.coroutines.launch
-
-class CredentialSelectorActivity : ComponentActivity() {
-
-    private val viewModel: CredentialSelectorViewModel by viewModels()
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        setTheme(android.R.style.Theme_DeviceDefault)
-
-        lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                viewModel.uiState.collect { uiState ->
-                    when (uiState) {
-                        CredentialSelectorUiState.Idle -> {
-                            // Don't display anything, assuming that there should be minimal latency
-                            // to parse the Credential Manager intent and define the state of the
-                            // app. If latency is big, then a "loading" screen should be displayed
-                            // to the user.
-                        }
-
-                        is CredentialSelectorUiState.Get -> {
-                            setContent {
-                                MaterialTheme {
-                                    WearApp()
-                                }
-                            }
-                        }
-
-                        CredentialSelectorUiState.Create -> {
-                            // TODO: b/301206624 - Implement create flow
-                            finish()
-                        }
-
-                        is CredentialSelectorUiState.Cancel -> {
-                            // TODO: b/300422310 - Implement cancel with message flow
-                            finish()
-                        }
-
-                        CredentialSelectorUiState.Finish -> {
-                            finish()
-                        }
-                    }
-                }
-            }
-        }
-
-        viewModel.onNewIntent(intent)
-    }
-
-    override fun onNewIntent(intent: Intent) {
-        super.onNewIntent(intent)
-
-        val previousIntent = getIntent()
-        setIntent(intent)
-
-        viewModel.onNewIntent(intent, previousIntent)
-    }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
deleted file mode 100644
index d22d5d1..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open 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.0N
- *
- * 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.credentialmanager.ui
-
-import android.app.Application
-import android.content.Intent
-import android.util.Log
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.parse
-import com.android.credentialmanager.ktx.appLabel
-import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.mapper.toGet
-import com.android.credentialmanager.ui.model.PasskeyUiModel
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.ui.mapper.toGet
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class CredentialSelectorViewModel(
-    private val application: Application
-) : AndroidViewModel(application = application) {
-
-    private val _uiState =
-        MutableStateFlow<CredentialSelectorUiState>(CredentialSelectorUiState.Idle)
-    val uiState: StateFlow<CredentialSelectorUiState> = _uiState
-
-    fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
-        viewModelScope.launch {
-            val request = intent.parse()
-            if (shouldFinishActivity(request = request, previousIntent = previousIntent)) {
-                _uiState.value = CredentialSelectorUiState.Finish
-            } else {
-                when (request) {
-                    is Request.Cancel -> {
-                        request.appPackageName?.let { appPackageName ->
-                            application.packageManager.appLabel(appPackageName)?.let { appLabel ->
-                                _uiState.value = CredentialSelectorUiState.Cancel(appLabel)
-                            } ?: run {
-                                Log.d(TAG,
-                                    "Received UI cancel request with an invalid package name.")
-                                _uiState.value = CredentialSelectorUiState.Finish
-                            }
-                        } ?: run {
-                            Log.d(TAG, "Received UI cancel request with an invalid package name.")
-                            _uiState.value = CredentialSelectorUiState.Finish
-                        }
-                    }
-
-                    Request.Create -> {
-                        _uiState.value = CredentialSelectorUiState.Create
-                    }
-
-                    is Request.Get -> {
-                        _uiState.value = request.toGet()
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Check if backend requested the UI activity to be cancelled. Different from the other
-     * finishing flows, this one does not report anything back to the Credential Manager service
-     * backend.
-     */
-    private fun shouldFinishActivity(request: Request, previousIntent: Intent? = null): Boolean {
-        if (request !is Request.Cancel) {
-            return false
-        } else {
-            Log.d(
-                TAG, "Received UI cancellation intent. Should show cancellation" +
-                " ui = ${request.showCancellationUi}")
-
-            previousIntent?.let {
-                val previousUiRequest = previousIntent.parse()
-
-                if (previousUiRequest is Request.Cancel) {
-                    val previousToken = previousIntent.requestInfo?.token
-                    val currentToken = previousIntent.requestInfo?.token
-
-                    if (previousToken != currentToken) {
-                        // Cancellation was for a different request, don't cancel the current UI.
-                        return false
-                    }
-                }
-            }
-
-            return !request.showCancellationUi
-        }
-    }
-}
-
-sealed class CredentialSelectorUiState {
-    data object Idle : CredentialSelectorUiState()
-    sealed class Get : CredentialSelectorUiState() {
-        data class SingleProviderSinglePasskey(val passkeyUiModel: PasskeyUiModel) : Get()
-        data class SingleProviderSinglePassword(val passwordUiModel: PasswordUiModel) : Get()
-
-        // TODO: b/301206470 add the remaining states
-    }
-
-    data object Create : CredentialSelectorUiState()
-    data class Cancel(val appName: String) : CredentialSelectorUiState()
-    data object Finish : CredentialSelectorUiState()
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
new file mode 100644
index 0000000..da5697d
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.ui
+
+import androidx.navigation.NavController
+
+fun NavController.navigateToLoading() {
+    navigate(Screen.Loading.route)
+}
+
+fun NavController.navigateToSinglePasswordScreen() {
+    navigate(Screen.SinglePasswordScreen.route)
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
index 7d1a49b..c3919a0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -19,5 +19,7 @@
 sealed class Screen(
     val route: String,
 ) {
-    data object Main : Screen("main")
+    data object Loading : Screen("loading")
+
+    data object SinglePasswordScreen : Screen("singlePasswordScreen")
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 19ea9ed..7e0ea30 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -19,28 +19,94 @@
 package com.android.credentialmanager.ui
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
-import com.android.credentialmanager.ui.screens.MainScreen
+import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.ui.screens.LoadingScreen
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.navscaffold.WearNavScaffold
 import com.google.android.horologist.compose.navscaffold.composable
+import com.google.android.horologist.compose.navscaffold.scrollable
 
 @Composable
-fun WearApp() {
+fun WearApp(
+    viewModel: CredentialSelectorViewModel,
+    onCloseApp: () -> Unit,
+) {
     val navController = rememberSwipeDismissableNavController()
     val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
     val navHostState =
         rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
 
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
     WearNavScaffold(
-        startDestination = Screen.Main.route,
+        startDestination = Screen.Loading.route,
         navController = navController,
         state = navHostState,
     ) {
-        composable(Screen.Main.route) {
-            MainScreen()
+        composable(Screen.Loading.route) {
+            LoadingScreen()
+        }
+
+        scrollable(Screen.SinglePasswordScreen.route) {
+            SinglePasswordScreen(
+                columnState = it.columnState,
+                onCloseApp = onCloseApp,
+            )
+        }
+    }
+
+    when (val state = uiState) {
+        CredentialSelectorUiState.Idle -> {
+            if (navController.currentDestination?.route != Screen.Loading.route) {
+                navController.navigateToLoading()
+            }
+        }
+
+        is CredentialSelectorUiState.Get -> {
+            handleGetNavigation(
+                navController = navController,
+                state = state,
+                onCloseApp = onCloseApp,
+            )
+        }
+
+        CredentialSelectorUiState.Create -> {
+            // TODO: b/301206624 - Implement create flow
+            onCloseApp()
+        }
+
+        is CredentialSelectorUiState.Cancel -> {
+            // TODO: b/300422310 - Implement cancel with message flow
+            onCloseApp()
+        }
+
+        CredentialSelectorUiState.Close -> {
+            onCloseApp()
+        }
+    }
+}
+
+private fun handleGetNavigation(
+    navController: NavController,
+    state: CredentialSelectorUiState.Get,
+    onCloseApp: () -> Unit,
+) {
+    when (state) {
+        is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+            navController.navigateToSinglePasswordScreen()
+        }
+
+        else -> {
+            // TODO: b/301206470 - Implement other get flows
+            onCloseApp()
         }
     }
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
deleted file mode 100644
index 5ceec178..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.credentialmanager.ui.mapper
-
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
-import com.android.credentialmanager.ui.CredentialSelectorUiState
-import com.android.credentialmanager.factory.fromSlice
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
-    if (this.providers.isEmpty()) {
-        throw IllegalStateException("Invalid GetCredential request with empty list of providers.")
-    }
-
-    if (this.entries.isEmpty()) {
-        throw IllegalStateException("Invalid GetCredential request with empty list of entries.")
-    }
-
-    if (this.providers.size == 1) {
-        if (this.entries.size == 1) {
-            val slice = this.entries.first().slice
-            when (val credentialEntry = fromSlice(slice)) {
-                is PasswordCredentialEntry -> {
-                    return CredentialSelectorUiState.Get.SingleProviderSinglePassword(
-                        PasswordUiModel(credentialEntry.displayName.toString())
-                    )
-                }
-
-                is PublicKeyCredentialEntry -> {
-                    TODO("b/301206470 - to be implemented")
-                }
-
-                is CustomCredentialEntry -> {
-                    TODO("b/301206470 - to be implemented")
-                }
-
-                else -> {
-                    throw IllegalStateException(
-                        "Encountered unrecognized credential entry (${slice.spec?.type}) for " +
-                            "GetCredential request with single account"
-                    )
-                }
-            }
-        } else {
-            TODO("b/301206470 - to be implemented")
-        }
-    } else {
-        TODO("b/301206470 - to be implemented")
-    }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
new file mode 100644
index 0000000..f2f878e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.ui.mappers
+
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.CredentialSelectorUiState
+
+fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+    // TODO: b/301206470 returning a hard coded state for MVP
+    if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
+
+    return if (providers.size == 1) {
+        if (passwordEntries.size == 1) {
+            CredentialSelectorUiState.Get.SingleProviderSinglePassword
+        } else {
+            TODO() // b/301206470 - Implement other get flows
+        }
+    } else {
+        TODO() // b/301206470 - Implement other get flows
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
similarity index 74%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
index 94a671e..b3ab0c4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
@@ -16,17 +16,15 @@
 
 package com.android.credentialmanager.ui.screens
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.wear.compose.material.Text
 
 @Composable
-fun MainScreen(
+fun LoadingScreen(
     modifier: Modifier = Modifier
 ) {
-    Box(modifier = modifier, contentAlignment = Alignment.Center) {
-        Text("This is a placeholder for the main screen.")
-    }
+    // Don't display anything, assuming that there should be minimal latency
+    // to parse the Credential Manager intent and define the state of the
+    // app. If latency is big, then a "loading" screen should be displayed
+    // to the user.
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
deleted file mode 100644
index d863d3c..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2023 The Android Open 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
- *
- *      https://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.
- */
-
-@file:OptIn(ExperimentalHorologistApi::class)
-
-package com.android.credentialmanager.ui.screens
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import com.android.credentialmanager.R
-import com.android.credentialmanager.ui.components.DialogButtonsRow
-import com.android.credentialmanager.ui.components.PasswordRow
-import com.android.credentialmanager.ui.components.SignInHeader
-import com.google.android.horologist.annotations.ExperimentalHorologistApi
-import com.google.android.horologist.compose.layout.ScalingLazyColumnState
-import com.google.android.horologist.compose.layout.belowTimeTextPreview
-import com.google.android.horologist.compose.tools.WearPreview
-
-@Composable
-fun SinglePasswordScreen(
-    email: String,
-    onCancelClick: () -> Unit,
-    onOKClick: () -> Unit,
-    columnState: ScalingLazyColumnState,
-    modifier: Modifier = Modifier,
-) {
-    SingleAccountScreen(
-        headerContent = {
-            SignInHeader(
-                icon = R.drawable.passkey_icon,
-                title = stringResource(R.string.use_password_title),
-            )
-        },
-        accountContent = {
-            PasswordRow(
-                email = email,
-                modifier = Modifier.padding(top = 10.dp),
-            )
-        },
-        columnState = columnState,
-        modifier = modifier.padding(horizontal = 10.dp)
-    ) {
-        item {
-            DialogButtonsRow(
-                onCancelClick = onCancelClick,
-                onOKClick = onOKClick,
-                modifier = Modifier.padding(top = 10.dp)
-            )
-        }
-    }
-}
-
-@WearPreview
-@Composable
-fun SinglePasswordScreenPreview() {
-    SinglePasswordScreen(
-        email = "beckett_bakery@gmail.com",
-        onCancelClick = {},
-        onOKClick = {},
-        columnState = belowTimeTextPreview(),
-    )
-}
-
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
similarity index 97%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
index f344ad0..8532783 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalHorologistApi::class)
 
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single
 
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
similarity index 94%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index c8f871e..c9b0230 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalHorologistApi::class)
 
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single.passkey
 
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
@@ -27,6 +27,7 @@
 import com.android.credentialmanager.ui.components.AccountRow
 import com.android.credentialmanager.ui.components.DialogButtonsRow
 import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.layout.ScalingLazyColumnState
 import com.google.android.horologist.compose.layout.belowTimeTextPreview
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
new file mode 100644
index 0000000..c885ec4
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2023 The Android Open 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
+ *
+ *      https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens.single.password
+
+import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.PasswordRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SinglePasswordScreen(
+    columnState: ScalingLazyColumnState,
+    onCloseApp: () -> Unit,
+    modifier: Modifier = Modifier,
+    viewModel: SinglePasswordScreenViewModel =
+        viewModel(factory = SinglePasswordScreenViewModel.Factory),
+) {
+    viewModel.initialize()
+
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
+    when (val state = uiState) {
+        SinglePasswordScreenUiState.Idle -> {
+            // TODO: b/301206470 implement latency version of the screen
+        }
+
+        is SinglePasswordScreenUiState.Loaded -> {
+            val model = state.passwordUiModel
+            SinglePasswordScreen(
+                email = model.email,
+                onCancelClick = viewModel::onCancelClick,
+                onOKClick = viewModel::onOKClick,
+                columnState = columnState,
+                modifier = modifier
+            )
+        }
+
+        is SinglePasswordScreenUiState.PasswordSelected -> {
+            val launcher = rememberLauncherForActivityResult(
+                StartBalIntentSenderForResultContract()
+            ) {
+                viewModel.onPasswordInfoRetrieved(it.resultCode, it.data)
+            }
+
+            SideEffect {
+                launcher.launch(state.intentSenderRequest)
+            }
+        }
+
+        SinglePasswordScreenUiState.Cancel -> {
+            // TODO: b/301206470 implement navigation for when user taps cancel
+        }
+
+        SinglePasswordScreenUiState.Error -> {
+            // TODO: b/301206470 implement navigation for when there is an error to load screen
+        }
+
+        SinglePasswordScreenUiState.Completed -> {
+            Log.d(TAG, "Received signal to finish the activity.")
+            onCloseApp()
+        }
+    }
+}
+
+@Composable
+fun SinglePasswordScreen(
+    email: String,
+    onCancelClick: () -> Unit,
+    onOKClick: () -> Unit,
+    columnState: ScalingLazyColumnState,
+    modifier: Modifier = Modifier,
+) {
+    SingleAccountScreen(
+        headerContent = {
+            SignInHeader(
+                icon = R.drawable.passkey_icon,
+                title = stringResource(R.string.use_password_title),
+            )
+        },
+        accountContent = {
+            PasswordRow(
+                email = email,
+                modifier = Modifier.padding(top = 10.dp),
+            )
+        },
+        columnState = columnState,
+        modifier = modifier.padding(horizontal = 10.dp)
+    ) {
+        item {
+            DialogButtonsRow(
+                onCancelClick = onCancelClick,
+                onOKClick = onOKClick,
+                modifier = Modifier.padding(top = 10.dp)
+            )
+        }
+    }
+}
+
+@WearPreview
+@Composable
+fun SinglePasswordScreenPreview() {
+    SinglePasswordScreen(
+        email = "beckett_bakery@gmail.com",
+        onCancelClick = {},
+        onOKClick = {},
+        columnState = belowTimeTextPreview(),
+    )
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
new file mode 100644
index 0000000..9b06622
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.0N
+ *
+ * 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.credentialmanager.ui.screens.single.password
+
+import android.content.Intent
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.ProviderPendingIntentResponse
+import android.credentials.ui.UserSelectionDialogResult
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.annotation.MainThread
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+class SinglePasswordScreenViewModel(
+    private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+    private var initializeCalled = false
+
+    private lateinit var requestGet: Request.Get
+    private lateinit var password: Password
+
+    private val _uiState =
+        MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
+    val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
+
+    @MainThread
+    fun initialize() {
+        if (initializeCalled) return
+        initializeCalled = true
+
+        viewModelScope.launch {
+            val request = requestRepository.requests.first()
+            Log.d(TAG, "request: $request")
+
+            if (request !is Request.Get) {
+                _uiState.value = SinglePasswordScreenUiState.Error
+            } else {
+                requestGet = request
+                if (requestGet.passwordEntries.isEmpty()) {
+                    Log.d(TAG, "Empty passwordEntries")
+                    _uiState.value = SinglePasswordScreenUiState.Error
+                } else {
+                    password = requestGet.passwordEntries.first()
+                    _uiState.value = SinglePasswordScreenUiState.Loaded(
+                        PasswordUiModel(
+                            email = password.passwordCredentialEntry.username.toString(),
+                        )
+                    )
+                }
+            }
+        }
+    }
+
+    fun onCancelClick() {
+        _uiState.value = SinglePasswordScreenUiState.Cancel
+    }
+
+    fun onOKClick() {
+        // TODO: b/301206470 move this code to shared module
+        val entryIntent = password.entry.frameworkExtrasIntent
+        entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, false)
+        val intentSenderRequest = IntentSenderRequest.Builder(
+            pendingIntent = password.passwordCredentialEntry.pendingIntent
+        ).setFillInIntent(entryIntent).build()
+
+        _uiState.value = SinglePasswordScreenUiState.PasswordSelected(
+            intentSenderRequest = intentSenderRequest
+        )
+    }
+
+    fun onPasswordInfoRetrieved(
+        resultCode: Int? = null,
+        resultData: Intent? = null,
+    ) {
+        // TODO: b/301206470 move this code to shared module
+        Log.d(TAG, "credential selected: {provider=${password.providerId}" +
+            ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
+
+        val userSelectionDialogResult = UserSelectionDialogResult(
+            requestGet.token,
+            password.providerId,
+            password.entry.key,
+            password.entry.subkey,
+            if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+        )
+        val resultDataBundle = Bundle()
+        UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+        requestGet.resultReceiver?.send(
+            BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+            resultDataBundle
+        )
+
+        _uiState.value = SinglePasswordScreenUiState.Completed
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+
+                return SinglePasswordScreenViewModel(
+                    requestRepository = (application as CredentialSelectorApp).requestRepository,
+                ) as T
+            }
+        }
+    }
+}
+
+sealed class SinglePasswordScreenUiState {
+    data object Idle : SinglePasswordScreenUiState()
+    data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
+    data class PasswordSelected(
+        val intentSenderRequest: IntentSenderRequest
+    ) : SinglePasswordScreenUiState()
+
+    data object Cancel : SinglePasswordScreenUiState()
+    data object Error : SinglePasswordScreenUiState()
+    data object Completed : SinglePasswordScreenUiState()
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5e7e044..104f3d2 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -235,6 +235,9 @@
     srcs: [
         /* Status bar fakes */
         "tests/src/com/android/systemui/statusbar/pipeline/airplane/data/repository/FakeAirplaneModeRepository.kt",
+        "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt",
+        "tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt",
+        "tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt",
         "tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt",
         "tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt",
 
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9bfc4be..b5b873c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -634,7 +634,7 @@
 
         <!-- started from MediaProjectionManager -->
         <activity
-            android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
+            android:name=".media.MediaProjectionPermissionActivity"
             android:exported="true"
             android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
             android:finishOnCloseSystemDialogs="true"
@@ -643,7 +643,7 @@
             android:visibleToInstantApps="true"/>
 
         <activity
-            android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
+            android:name=".media.MediaProjectionAppSelectorActivity"
             android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index af6fa86..5c2f979 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -131,5 +131,22 @@
         }
       ]
     }
+  ],
+  // v2/sysui/suite/test-mapping-sysui-screenshot-test
+  "sysui-screenshot-test": [
+    {
+      "name": "SystemUIGoogleScreenshotTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        }
+      ]
+    }
   ]
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 8e34008..519c0a9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -39,7 +39,9 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.ElementKey
@@ -166,11 +168,18 @@
                 modifier =
                     Modifier.align(Alignment.CenterVertically)
                         // use graphicsLayer instead of Modifier.scale to anchor transform to
-                        // top left corner
+                        // the (start, top) corner
                         .graphicsLayer(
                             scaleX = 2.57f,
                             scaleY = 2.57f,
-                            transformOrigin = TransformOrigin(0f, 0.5f)
+                            transformOrigin =
+                                TransformOrigin(
+                                    when (LocalLayoutDirection.current) {
+                                        LayoutDirection.Ltr -> 0f
+                                        LayoutDirection.Rtl -> 1f
+                                    },
+                                    0.5f
+                                )
                         ),
             )
             Spacer(modifier = Modifier.weight(1f))
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
index 7ce1ba3..d7d75d4 100644
--- a/packages/SystemUI/res/drawable/volume_row_seekbar.xml
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -20,17 +20,14 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:paddingMode="stack" >
+    <!-- The groove used for indicating max volume !-->
     <item android:id="@android:id/background"
         android:gravity="center_vertical|fill_horizontal">
-        <inset
-            android:insetLeft="@dimen/rounded_slider_track_inset"
-            android:insetRight="@dimen/rounded_slider_track_inset" >
-            <shape>
-                <size android:height="@dimen/volume_dialog_track_width" />
-                <corners android:radius="@dimen/volume_dialog_track_corner_radius" />
-                <solid android:color="?androidprv:attr/colorAccentSecondaryVariant" />
-            </shape>
-        </inset>
+        <shape>
+            <size android:height="@dimen/volume_dialog_track_width" />
+            <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+            <solid android:color="?androidprv:attr/materialColorOutlineVariant" />
+        </shape>
     </item>
     <item android:id="@android:id/progress"
         android:gravity="center_vertical|fill_horizontal">
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 37b8ae0..c70f8e2 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -22,8 +22,7 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|bottom"
         android:paddingStart="4dp"
-        android:paddingEnd="4dp"
->
+        android:paddingEnd="4dp">
 
     <LinearLayout
         android:id="@+id/half_shelf"
@@ -82,11 +81,21 @@
                     android:theme="@style/MainSwitch.Settingslib"/>
             </com.android.systemui.statusbar.notification.row.AppControlView>
 
-            <!-- ChannelRows get added dynamically -->
-
+            <ScrollView
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/notification_blocker_channel_list_height"
+                android:clipToPadding="false">
+                <LinearLayout
+                    android:id="@+id/scrollView"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical">
+                    <!-- ChannelRows get added dynamically -->
+                </LinearLayout>
+            </ScrollView>
         </com.android.systemui.statusbar.notification.row.ChannelEditorListView>
 
-        <RelativeLayout
+        <LinearLayout
             android:id="@+id/bottom_actions"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -98,25 +107,23 @@
                 android:text="@string/see_more_title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:gravity="start|center_vertical"
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
                 android:maxWidth="200dp"
                 style="@style/Widget.Dialog.Button"/>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1" />
             <TextView
                 android:id="@+id/done_button"
                 android:text="@string/inline_ok_button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_centerVertical="true"
-                android:gravity="end|center_vertical"
                 android:maxWidth="125dp"
                 android:minWidth="@dimen/notification_importance_toggle_size"
                 android:minHeight="@dimen/notification_importance_toggle_size"
-                android:layout_alignParentEnd="true"
                 style="@style/Widget.Dialog.Button"/>
-        </RelativeLayout>
+        </LinearLayout>
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 259b9ad..cfb4017 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -89,6 +89,9 @@
     <dimen name="global_actions_button_size">72dp</dimen>
     <dimen name="global_actions_button_padding">26dp</dimen>
 
+    <!-- scroll view the size of 2 channel rows -->
+    <dimen name="notification_blocker_channel_list_height">128dp</dimen>
+
     <dimen name="keyguard_indication_margin_bottom">8dp</dimen>
     <dimen name="lock_icon_margin_bottom">24dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 88726af..0ee5da2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -313,6 +313,8 @@
     <!-- The space around a notification menu item  -->
     <dimen name="notification_menu_icon_padding">20dp</dimen>
 
+    <!-- scroll view the size of 3 channel rows -->
+    <dimen name="notification_blocker_channel_list_height">192dp</dimen>
     <!-- The vertical space around the buttons in the inline settings -->
     <dimen name="notification_guts_button_spacing">12dp</dimen>
 
@@ -545,7 +547,7 @@
     <!-- (volume_dialog_panel_width - rounded_slider_icon_size) / 2 -->
     <dimen name="volume_slider_icon_inset">11dp</dimen>
 
-    <dimen name="volume_dialog_track_width">4dp</dimen>
+    <dimen name="volume_dialog_track_width">40dp</dimen>
 
     <dimen name="volume_dialog_track_corner_radius">2dp</dimen>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
index 96a974d..7b2e1af 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt
@@ -19,6 +19,12 @@
 import android.os.Trace
 import android.os.TraceNameSupplier
 import java.util.concurrent.atomic.AtomicInteger
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.async
 
 /**
  * Run a block within a [Trace] section. Calls [Trace.beginSection] before and [Trace.endSection]
@@ -85,5 +91,18 @@
                 Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, cookie)
             }
         }
+
+        /**
+         * Convenience method to avoid one indentation level when we want to add a trace when
+         * launching a coroutine
+         */
+        fun <T> CoroutineScope.tracedAsync(
+            method: String,
+            context: CoroutineContext = EmptyCoroutineContext,
+            start: CoroutineStart = CoroutineStart.DEFAULT,
+            block: suspend () -> T
+        ): Deferred<T> {
+            return async(context, start) { traceAsync(method) { block() } }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 46d3c8a..79d9c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -1096,8 +1096,19 @@
             // cancel the fingerprint scan.
             mCancelAodFingerUpAction = mFgExecutor.executeDelayed(this::tryAodSendFingerUp,
                     AOD_SEND_FINGER_UP_DELAY_MILLIS);
-            // using a hard-coded value for major and minor until it is available from the sensor
-            onFingerDown(requestId, screenX, screenY, minor, major);
+            // using a hard-coded value for orientation, time and gestureStart until they are
+            // available from the sensor.
+            onFingerDown(
+                    requestId,
+                    MotionEvent.INVALID_POINTER_ID /* pointerId */,
+                    screenX,
+                    screenY,
+                    minor,
+                    major,
+                    0f /* orientation */,
+                    0L /* time */,
+                    0L /* gestureStart */,
+                    true /* isAod */);
         };
 
         if (mScreenOn) {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index bae0ac7..f3a463b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -235,17 +235,16 @@
         repository.setMessage(errorMessage(authenticationInteractor.getAuthenticationMethod()))
     }
 
-    /** If the bouncer is showing, hides the bouncer and return to the lockscreen scene. */
-    fun hide(
-        loggingReason: String,
-    ) {
+    /** Notifies the interactor that the input method editor has been hidden. */
+    fun onImeHidden() {
+        // If the bouncer is showing, hide it and return to the lockscreen scene.
         if (sceneInteractor.desiredScene.value.key != SceneKey.Bouncer) {
             return
         }
 
         sceneInteractor.changeScene(
             scene = SceneModel(SceneKey.Lockscreen),
-            loggingReason = loggingReason,
+            loggingReason = "IME hidden",
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 0b0a8f5..66c6162 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -78,10 +78,7 @@
      */
     fun onImeVisibilityChanged(isVisible: Boolean) {
         if (isImeVisible && !isVisible) {
-            // The IME has gone from visible to invisible, dismiss the bouncer.
-            interactor.hide(
-                loggingReason = "IME hidden",
-            )
+            interactor.onImeHidden()
         }
 
         isImeVisible = isVisible
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 4b38267..3b70555 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -298,6 +298,11 @@
     @JvmField val MIGRATE_KEYGUARD_STATUS_BAR_VIEW =
         unreleasedFlag("migrate_keyguard_status_bar_view")
 
+    /** Migrate clocks from keyguard status view to keyguard root view*/
+    // TODO(b/301502635): Tracking Bug.
+    @JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
+            unreleasedFlag("migrate_clocks_to_blueprint")
+
     /** Enables preview loading animation in the wallpaper picker. */
     // TODO(b/274443705): Tracking Bug
     @JvmField
@@ -763,6 +768,9 @@
     // TODO(b/289573946): Tracking Bug
     @JvmField val PRECOMPUTED_TEXT = unreleasedFlag("precomputed_text", teamfood = true)
 
+    // TODO(b/302087895): Tracking Bug
+    @JvmField val CALL_LAYOUT_ASYNC_SET_DATA = unreleasedFlag("call_layout_async_set_data")
+
     // 2900 - CentralSurfaces-related flags
 
     // TODO(b/285174336): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 06cf723..e8740a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -26,7 +26,6 @@
 import android.util.Log
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository
 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
@@ -40,6 +39,8 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.user.data.repository.UserRepository
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -133,6 +134,7 @@
     devicePostureRepository: DevicePostureRepository,
     facePropertyRepository: FacePropertyRepository,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
+    mobileConnectionsRepository: MobileConnectionsRepository,
     dumpManager: DumpManager,
 ) : BiometricSettingsRepository, Dumpable {
 
@@ -346,14 +348,15 @@
             .and(isFingerprintBiometricAllowed)
             .stateIn(scope, SharingStarted.Eagerly, false)
 
-    override val isFaceAuthEnrolledAndEnabled: Flow<Boolean>
-        get() = isFaceAuthenticationEnabled.and(isFaceEnrolled)
+    override val isFaceAuthEnrolledAndEnabled: Flow<Boolean> =
+        isFaceAuthenticationEnabled
+            .and(isFaceEnrolled)
+            .and(mobileConnectionsRepository.isAnySimSecure.isFalse())
 
-    override val isFaceAuthCurrentlyAllowed: Flow<Boolean>
-        get() =
-            isFaceAuthEnrolledAndEnabled
-                .and(isFaceBiometricsAllowed)
-                .and(isFaceAuthSupportedInCurrentPosture)
+    override val isFaceAuthCurrentlyAllowed: Flow<Boolean> =
+        isFaceAuthEnrolledAndEnabled
+            .and(isFaceBiometricsAllowed)
+            .and(isFaceAuthSupportedInCurrentPosture)
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -426,3 +429,5 @@
 
 private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
     this.combine(anotherFlow) { a, b -> a && b }
+
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> = this.map { !it }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index dd7eee9..abc30ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -25,6 +25,8 @@
 import android.view.ViewGroup
 import android.view.ViewPropertyAnimator
 import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
 import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
@@ -383,6 +385,27 @@
                     falsingManager,
                 )
                 view.setOnTouchListener(onTouchListener)
+                view.setOnClickListener {
+                    messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+                    val amplitude =
+                        view.context.resources
+                            .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+                            .toFloat()
+                    val shakeAnimator =
+                        ObjectAnimator.ofFloat(
+                            view,
+                            "translationX",
+                            -amplitude / 2,
+                            amplitude / 2,
+                        )
+                    shakeAnimator.duration =
+                        KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+                    shakeAnimator.interpolator =
+                        CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+                    shakeAnimator.start()
+
+                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+                }
                 view.onLongClickListener =
                     OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 125e2da..f2d39da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -99,41 +99,7 @@
                     // When not using a stylus, lifting the finger/pointer will actually cancel
                     // the long-press gesture. Calling cancel after the quick affordance was
                     // already long-press activated is a no-op, so it's safe to call from here.
-                    cancel(
-                        onAnimationEnd =
-                            if (event.eventTime - event.downTime < longPressDurationMs) {
-                                Runnable {
-                                    messageDisplayer.invoke(
-                                        R.string.keyguard_affordance_press_too_short
-                                    )
-                                    val amplitude =
-                                        view.context.resources
-                                            .getDimensionPixelSize(
-                                                R.dimen.keyguard_affordance_shake_amplitude
-                                            )
-                                            .toFloat()
-                                    val shakeAnimator =
-                                        ObjectAnimator.ofFloat(
-                                            view,
-                                            "translationX",
-                                            -amplitude / 2,
-                                            amplitude / 2,
-                                        )
-                                    shakeAnimator.duration =
-                                        KeyguardBottomAreaVibrations.ShakeAnimationDuration
-                                            .inWholeMilliseconds
-                                    shakeAnimator.interpolator =
-                                        CycleInterpolator(
-                                            KeyguardBottomAreaVibrations.ShakeAnimationCycles
-                                        )
-                                    shakeAnimator.start()
-
-                                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
-                                }
-                            } else {
-                                null
-                            }
-                    )
+                    cancel()
                 }
                 false
             }
@@ -168,10 +134,10 @@
         view.setOnClickListener(null)
     }
 
-    fun cancel(onAnimationEnd: Runnable? = null) {
+    fun cancel() {
         longPressAnimator?.cancel()
         longPressAnimator = null
-        view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
+        view.animate().scaleX(1f).scaleY(1f)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index eeb4ac3..aa76702 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -23,6 +23,8 @@
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
+import androidx.core.animation.CycleInterpolator
+import androidx.core.animation.ObjectAnimator
 import androidx.core.view.isInvisible
 import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
@@ -216,6 +218,27 @@
                     falsingManager,
                 )
                 view.setOnTouchListener(onTouchListener)
+                view.setOnClickListener {
+                    messageDisplayer.invoke(R.string.keyguard_affordance_press_too_short)
+                    val amplitude =
+                        view.context.resources
+                            .getDimensionPixelSize(R.dimen.keyguard_affordance_shake_amplitude)
+                            .toFloat()
+                    val shakeAnimator =
+                        ObjectAnimator.ofFloat(
+                            view,
+                            "translationX",
+                            -amplitude / 2,
+                            amplitude / 2,
+                        )
+                    shakeAnimator.duration =
+                        KeyguardBottomAreaVibrations.ShakeAnimationDuration.inWholeMilliseconds
+                    shakeAnimator.interpolator =
+                        CycleInterpolator(KeyguardBottomAreaVibrations.ShakeAnimationCycles)
+                    shakeAnimator.start()
+
+                    vibratorHelper?.vibrate(KeyguardBottomAreaVibrations.Shake)
+                }
                 view.onLongClickListener =
                     OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 6c2ce7f..1943b34 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -29,6 +29,7 @@
 import com.android.systemui.log.LogcatEchoTrackerProd;
 import com.android.systemui.log.table.TableLogBuffer;
 import com.android.systemui.log.table.TableLogBufferFactory;
+import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.wakelock.WakeLockLog;
@@ -229,12 +230,12 @@
     }
 
     /**
-     * Provides a logging buffer for logs related to {@link com.android.systemui.qs.QSFragment}'s
+     * Provides a logging buffer for logs related to {@link QSFragmentLegacy}'s
      * disable flag adjustments.
      */
     @Provides
     @SysUISingleton
-    @QSFragmentDisableLog
+    @QSDisableLog
     public static LogBuffer provideQSFragmentDisableLogBuffer(LogBufferFactory factory) {
         return factory.create("QSFragmentDisableFlagsLog", 10 /* maxSize */,
                 false /* systrace */);
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
rename to packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
index 557a254..b3bceca 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSFragmentDisableLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSDisableLog.java
@@ -19,6 +19,7 @@
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import com.android.systemui.log.LogBuffer;
+import com.android.systemui.qs.QSFragmentLegacy;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
@@ -27,10 +28,10 @@
 
 /**
  * A {@link LogBuffer} for disable flag adjustments made in
- * {@link com.android.systemui.qs.QSFragment}.
+ * {@link QSFragmentLegacy}.
  */
 @Qualifier
 @Documented
 @Retention(RUNTIME)
-public @interface QSFragmentDisableLog {
+public @interface QSDisableLog {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index b5d3e91..88bc064 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.mediaprojection.appselector
+package com.android.systemui.media
 
 import android.app.ActivityOptions
 import android.content.Intent
@@ -46,11 +46,13 @@
 import com.android.internal.widget.RecyclerView
 import com.android.internal.widget.RecyclerViewAccessibilityDelegate
 import com.android.internal.widget.ResolverDrawerLayout
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
-import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
+import com.android.systemui.res.R
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.mediaprojection.appselector.view.MediaProjectionRecentsViewController
-import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.AsyncActivityLauncher
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
similarity index 80%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
index 11d0be5..fbf9294 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
@@ -14,17 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection
+package com.android.systemui.media
 
 import android.os.IBinder
 import android.os.Parcel
 import android.os.Parcelable
 
 /**
- * Class that represents an area that should be captured. Currently it has only a launch cookie that
- * represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
+ * Class that represents an area that should be captured.
+ * Currently it has only a launch cookie that represents a task but
+ * we potentially could add more identifiers e.g. for a pair of tasks.
  */
-data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable {
+data class MediaProjectionCaptureTarget(
+    val launchCookie: IBinder?
+): Parcelable {
 
     constructor(parcel: Parcel) : this(parcel.readStrongBinder())
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 2b56d0c..4de6278 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection.permission;
+package com.android.systemui.media;
 
 import static android.media.projection.IMediaProjectionManager.EXTRA_PACKAGE_REUSING_GRANTED_CONSENT;
 import static android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTED_CONSENT;
@@ -22,8 +22,8 @@
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
-import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
-import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.SINGLE_APP;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.ENTIRE_SCREEN;
+import static com.android.systemui.screenrecord.ScreenShareOptionKt.SINGLE_APP;
 
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -51,13 +51,13 @@
 import android.util.Log;
 import android.view.Window;
 
+import com.android.systemui.res.R;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
-import com.android.systemui.mediaprojection.MediaProjectionServiceHelper;
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
-import com.android.systemui.res.R;
+import com.android.systemui.screenrecord.MediaProjectionPermissionDialog;
+import com.android.systemui.screenrecord.ScreenShareOption;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.Utils;
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
rename to packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
index f1cade7..9e616e2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionServiceHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionServiceHelper.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection
+package com.android.systemui.media
 
 import android.content.Context
 import android.media.projection.IMediaProjection
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 72aea04..33d9cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -23,6 +23,8 @@
 import androidx.lifecycle.DefaultLifecycleObserver
 import com.android.launcher3.icons.IconFactory
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionPermissionActivity
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader
 import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
 import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
@@ -35,7 +37,6 @@
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider
 import com.android.systemui.mediaprojection.devicepolicy.MediaProjectionDevicePolicyModule
 import com.android.systemui.mediaprojection.devicepolicy.PersonalProfile
-import com.android.systemui.mediaprojection.permission.MediaProjectionPermissionActivity
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.policy.ConfigurationController
 import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 463c79c..eba1c25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -28,7 +28,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
@@ -86,8 +86,7 @@
     private final QuickQSPanel mQuickQsPanel;
     private final QSPanelController mQsPanelController;
     private final QuickQSPanelController mQuickQSPanelController;
-    private final QuickStatusBarHeader mQuickStatusBarHeader;
-    private final QS mQs;
+    private final View mQsRootView;
 
     @Nullable
     private PagedTileLayout mPagedLayout;
@@ -115,8 +114,6 @@
     // Brightness slider opacity driver. Uses linear interpolator.
     @Nullable
     private TouchAnimator mBrightnessOpacityAnimator;
-    // Animator for Footer actions in QQS
-    private TouchAnimator mQQSFooterActionsAnimator;
     // Height animator for QQS tiles (height changing from QQS size to QS size)
     @Nullable
     private HeightExpansionAnimator mQQSTileHeightAnimator;
@@ -144,22 +141,21 @@
     private int[] mTmpLoc2 = new int[2];
 
     @Inject
-    public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader,
+    public QSAnimator(@RootView View rootView, QuickQSPanel quickPanel,
             QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController, QSHost qsTileHost,
             @Main Executor executor, TunerService tunerService,
             QSExpansionPathInterpolator qsExpansionPathInterpolator) {
-        mQs = qs;
+        mQsRootView = rootView;
         mQuickQsPanel = quickPanel;
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
-        mQuickStatusBarHeader = quickStatusBarHeader;
         mHost = qsTileHost;
         mExecutor = executor;
         mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
         mHost.addCallback(this);
         mQsPanelController.addOnAttachStateChangeListener(this);
-        qs.getView().addOnLayoutChangeListener(this);
+        mQsRootView.addOnLayoutChangeListener(this);
         if (mQsPanelController.isAttachedToWindow()) {
             onViewAttachedToWindow(null);
         }
@@ -314,8 +310,7 @@
                     break;
                 }
 
-                final View tileIcon = tileView.getIcon().getIconView();
-                View view = mQs.getView();
+                View view = mQsRootView;
 
                 // This case: less tiles to animate in small displays.
                 if (count < mQuickQSPanelController.getTileLayout().getNumVisibleTiles()) {
@@ -480,7 +475,7 @@
                 .setStartDelay(QS_TILE_LABEL_FADE_OUT_START)
                 .setEndDelay(QS_TILE_LABEL_FADE_OUT_END);
         SideLabelTileLayout qqsLayout = (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
-        View view = mQs.getView();
+        View view = mQsRootView;
         List<String> specs = mPagedLayout.getSpecsForPage(page);
         if (specs.isEmpty()) {
             // specs should not be empty in a valid secondary page, as we scrolled to it.
@@ -577,7 +572,7 @@
 
             // For (1), compute the distance via the vertical distance between QQS and QS tile
             // layout top.
-            View quickSettingsRootView = mQs.getView();
+            View quickSettingsRootView = mQsRootView;
             View qsTileLayout = (View) mQsPanelController.getTileLayout();
             View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
             getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
@@ -607,7 +602,7 @@
     private int getRelativeTranslationY(View view1, View view2) {
         int[] qsPosition = new int[2];
         int[] qqsPosition = new int[2];
-        View commonView = mQs.getView();
+        View commonView = mQsRootView;
         getRelativePositionInt(qsPosition, view1, commonView);
         getRelativePositionInt(qqsPosition, view2, commonView);
         return qsPosition[1] - qqsPosition[1];
@@ -690,9 +685,6 @@
         if (mBrightnessTranslationAnimator != null) {
             mBrightnessTranslationAnimator.setPosition(position);
         }
-        if (mQQSFooterActionsAnimator != null) {
-            mQQSFooterActionsAnimator.setPosition(position);
-        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
similarity index 73%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
rename to packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
index 6563e42..6f6f467 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDisableFlagsLogger.kt
@@ -1,20 +1,22 @@
 package com.android.systemui.qs
 
-import com.android.systemui.log.dagger.QSFragmentDisableLog
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.QSDisableLog
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
 import javax.inject.Inject
 
-/** A helper class for logging disable flag changes made in [QSFragment]. */
-class QSFragmentDisableFlagsLogger @Inject constructor(
-    @QSFragmentDisableLog private val buffer: LogBuffer,
+/** A helper class for logging disable flag changes made in [QSImpl]. */
+class QSDisableFlagsLogger
+@Inject
+constructor(
+    @QSDisableLog private val buffer: LogBuffer,
     private val disableFlagsLogger: DisableFlagsLogger
 ) {
 
     /**
-     * Logs a string representing the new state received by [QSFragment] and any modifications that
-     * were made to the flags locally.
+     * Logs a string representing the new state received by [QSImpl] and any modifications that were
+     * made to the flags locally.
      *
      * @param new see [DisableFlagsLogger.getDisableFlagsString]
      * @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString]
@@ -43,4 +45,4 @@
     }
 }
 
-private const val TAG = "QSFragmentDisableFlagsLog"
+private const val TAG = "QSDisableFlagsLog"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
new file mode 100644
index 0000000..8589ae9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2023 The Android Open 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;
+
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.qs.QSContainerController;
+import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.LifecycleFragment;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+public class QSFragmentLegacy extends LifecycleFragment implements QS {
+
+    private final Provider<QSImpl> mQsImplProvider;
+
+    private final QSFragmentComponent.Factory mQsComponentFactory;
+
+    @Nullable
+    private QSImpl mQsImpl;
+
+    @Inject
+    public QSFragmentLegacy(
+            Provider<QSImpl> qsImplProvider,
+            QSFragmentComponent.Factory qsComponentFactory
+    ) {
+        mQsComponentFactory = qsComponentFactory;
+        mQsImplProvider = qsImplProvider;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+            Bundle savedInstanceState) {
+        try {
+            Trace.beginSection("QSFragment#onCreateView");
+            inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
+                    R.style.Theme_SystemUI_QuickSettings));
+            return inflater.inflate(R.layout.qs_panel, container, false);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
+        mQsImpl = mQsImplProvider.get();
+        mQsImpl.onComponentCreated(qsFragmentComponent, savedInstanceState);
+    }
+
+    @Override
+    public void setScrollListener(ScrollListener listener) {
+        if (mQsImpl != null) {
+            mQsImpl.setScrollListener(listener);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (mQsImpl != null) {
+            mQsImpl.onCreate(savedInstanceState);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mQsImpl != null) {
+            mQsImpl.onDestroy();
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mQsImpl != null) {
+            mQsImpl.onSaveInstanceState(outState);
+        }
+    }
+
+    @Override
+    public View getHeader() {
+        if (mQsImpl != null) {
+            return mQsImpl.getHeader();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void setHasNotifications(boolean hasNotifications) {
+        if (mQsImpl != null) {
+            mQsImpl.setHasNotifications(hasNotifications);
+        }
+    }
+
+    @Override
+    public void setPanelView(HeightListener panelView) {
+        if (mQsImpl != null) {
+            mQsImpl.setPanelView(panelView);
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mQsImpl != null) {
+            mQsImpl.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
+    public void setFancyClipping(int leftInset, int top, int rightInset, int bottom,
+            int cornerRadius, boolean visible, boolean fullWidth) {
+        if (mQsImpl != null) {
+            mQsImpl.setFancyClipping(leftInset, top, rightInset, bottom, cornerRadius, visible,
+                    fullWidth);
+        }
+    }
+
+    @Override
+    public boolean isFullyCollapsed() {
+        if (mQsImpl != null) {
+            return mQsImpl.isFullyCollapsed();
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public void setCollapsedMediaVisibilityChangedListener(Consumer<Boolean> listener) {
+        if (mQsImpl != null) {
+            mQsImpl.setCollapsedMediaVisibilityChangedListener(listener);
+        }
+    }
+
+    @Override
+    public void setContainerController(QSContainerController controller) {
+        if (mQsImpl != null) {
+            mQsImpl.setContainerController(controller);
+        }
+    }
+
+    @Override
+    public boolean isCustomizing() {
+        if (mQsImpl != null) {
+            return mQsImpl.isCustomizing();
+        } else {
+            return false;
+        }
+    }
+
+    public QSPanelController getQSPanelController() {
+        if (mQsImpl != null) {
+            return mQsImpl.getQSPanelController();
+        } else {
+            return null;
+        }
+    }
+
+    public void setBrightnessMirrorController(
+            BrightnessMirrorController brightnessMirrorController) {
+        if (mQsImpl != null) {
+            mQsImpl.setBrightnessMirrorController(brightnessMirrorController);
+        }
+    }
+
+    @Override
+    public boolean isShowingDetail() {
+        if (mQsImpl != null) {
+            return mQsImpl.isShowingDetail();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void setHeaderClickable(boolean clickable) {
+        if (mQsImpl != null) {
+            mQsImpl.setHeaderClickable(clickable);
+        }
+    }
+
+    @Override
+    public void setExpanded(boolean expanded) {
+        if (mQsImpl != null) {
+            mQsImpl.setExpanded(expanded);
+        }
+    }
+
+    @Override
+    public void setOverscrolling(boolean stackScrollerOverscrolling) {
+        if (mQsImpl != null) {
+            mQsImpl.setOverscrolling(stackScrollerOverscrolling);
+        }
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (mQsImpl != null) {
+            mQsImpl.setListening(listening);
+        }
+    }
+
+    @Override
+    public void setQsVisible(boolean visible) {
+        if (mQsImpl != null) {
+            mQsImpl.setQsVisible(visible);
+        }
+    }
+
+    @Override
+    public void setHeaderListening(boolean listening) {
+        if (mQsImpl != null) {
+            mQsImpl.setHeaderListening(listening);
+        }
+    }
+
+    @Override
+    public void notifyCustomizeChanged() {
+        if (mQsImpl != null) {
+            mQsImpl.notifyCustomizeChanged();
+        }
+    }
+
+    @Override
+    public void setInSplitShade(boolean inSplitShade) {
+        if (mQsImpl != null) {
+            mQsImpl.setInSplitShade(inSplitShade);
+        }
+    }
+
+    @Override
+    public void setTransitionToFullShadeProgress(
+            boolean isTransitioningToFullShade,
+            @FloatRange(from = 0.0, to = 1.0) float qsTransitionFraction,
+            @FloatRange(from = 0.0, to = 1.0) float qsSquishinessFraction) {
+        if (mQsImpl != null) {
+            mQsImpl.setTransitionToFullShadeProgress(isTransitioningToFullShade,
+                    qsTransitionFraction, qsSquishinessFraction);
+        }
+    }
+
+    @Override
+    public void setOverScrollAmount(int overScrollAmount) {
+        if (mQsImpl != null) {
+            mQsImpl.setOverScrollAmount(overScrollAmount);
+        }
+    }
+
+    @Override
+    public int getHeightDiff() {
+        if (mQsImpl != null) {
+            return mQsImpl.getHeightDiff();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void setIsNotificationPanelFullWidth(boolean isFullWidth) {
+        if (mQsImpl != null) {
+            mQsImpl.setIsNotificationPanelFullWidth(isFullWidth);
+        }
+    }
+
+    @Override
+    public void setQsExpansion(float expansion, float panelExpansionFraction,
+            float proposedTranslation, float squishinessFraction) {
+        if (mQsImpl != null) {
+            mQsImpl.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+                    squishinessFraction);
+        }
+    }
+
+    @Override
+    public void animateHeaderSlidingOut() {
+        if (mQsImpl != null) {
+            mQsImpl.animateHeaderSlidingOut();
+        }
+    }
+
+    @Override
+    public void setCollapseExpandAction(Runnable action) {
+        if (mQsImpl != null) {
+            mQsImpl.setCollapseExpandAction(action);
+        }
+    }
+
+    @Override
+    public void closeDetail() {
+        if (mQsImpl != null) {
+            mQsImpl.closeDetail();
+        }
+    }
+
+    @Override
+    public void closeCustomizer() {
+        if (mQsImpl != null) {
+            mQsImpl.closeDetail();
+        }
+    }
+
+    /**
+     * The height this view wants to be. This is different from {@link View#getMeasuredHeight} such
+     * that during closing the detail panel, this already returns the smaller height.
+     */
+    @Override
+    public int getDesiredHeight() {
+        if (mQsImpl != null) {
+            return mQsImpl.getDesiredHeight();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void setHeightOverride(int desiredHeight) {
+        if (mQsImpl != null) {
+            mQsImpl.setHeightOverride(desiredHeight);
+        }
+    }
+
+    @Override
+    public int getQsMinExpansionHeight() {
+        if (mQsImpl != null) {
+            return mQsImpl.getQsMinExpansionHeight();
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public void hideImmediately() {
+        if (mQsImpl != null) {
+            mQsImpl.hideImmediately();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
index 253560b..9fa6769 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -31,10 +31,13 @@
 @Inject
 constructor(
     private val fragmentService: FragmentService,
-    private val qsFragmentProvider: Provider<QSFragment>
+    private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
 ) : CoreStartable {
     override fun start() {
-        fragmentService.addFragmentInstantiationProvider(QSFragment::class.java, qsFragmentProvider)
+        fragmentService.addFragmentInstantiationProvider(
+            QSFragmentLegacy::class.java,
+            qsFragmentLegacyProvider
+        )
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
rename to packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index fd81e9a..a32a024 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -1,15 +1,17 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open 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;
@@ -20,21 +22,18 @@
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-import static com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Trace;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
 import android.widget.LinearLayout;
 
 import androidx.annotation.FloatRange;
@@ -47,7 +46,6 @@
 import com.android.app.animation.Interpolators;
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.compose.ComposeFacade;
 import com.android.systemui.dump.DumpManager;
@@ -58,19 +56,20 @@
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
-import com.android.systemui.util.LifecycleFragment;
 import com.android.systemui.util.Utils;
 
 import java.io.PrintWriter;
@@ -80,8 +79,8 @@
 import javax.inject.Inject;
 import javax.inject.Named;
 
-public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks,
-        StatusBarStateController.StateListener, Dumpable {
+public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateController.StateListener,
+        Dumpable {
     private static final String TAG = "QS";
     private static final boolean DEBUG = false;
     private static final String EXTRA_EXPANDED = "expanded";
@@ -113,8 +112,7 @@
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final MediaHost mQsMediaHost;
     private final MediaHost mQqsMediaHost;
-    private final QSFragmentComponent.Factory mQsComponentFactory;
-    private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
+    private final QSDisableFlagsLogger mQsDisableFlagsLogger;
     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
     private final FeatureFlags mFeatureFlags;
     private final QSLogger mLogger;
@@ -167,14 +165,17 @@
 
     private boolean mIsSmallScreen;
 
+    private CommandQueue mCommandQueue;
+
+    private View mRootView;
+
     @Inject
-    public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
+    public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
             SysuiStatusBarStateController statusBarStateController, CommandQueue commandQueue,
             @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
             KeyguardBypassController keyguardBypassController,
-            QSFragmentComponent.Factory qsComponentFactory,
-            QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
+            QSDisableFlagsLogger qsDisableFlagsLogger,
             DumpManager dumpManager, QSLogger qsLogger,
             FooterActionsController footerActionsController,
             FooterActionsViewModel.Factory footerActionsViewModelFactory,
@@ -184,12 +185,11 @@
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mQsMediaHost = qsMediaHost;
         mQqsMediaHost = qqsMediaHost;
-        mQsComponentFactory = qsComponentFactory;
-        mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
+        mQsDisableFlagsLogger = qsDisableFlagsLogger;
         mLogger = qsLogger;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
         mFeatureFlags = featureFlags;
-        commandQueue.observe(getLifecycle(), this);
+        mCommandQueue = commandQueue;
         mBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
         mDumpManager = dumpManager;
@@ -199,34 +199,23 @@
         mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
     }
 
-    @Override
-    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-            Bundle savedInstanceState) {
-        try {
-            Trace.beginSection("QSFragment#onCreateView");
-            inflater = inflater.cloneInContext(new ContextThemeWrapper(getContext(),
-                    R.style.Theme_SystemUI_QuickSettings));
-            return inflater.inflate(R.layout.qs_panel, container, false);
-        } finally {
-            Trace.endSection();
-        }
-    }
+    public void onComponentCreated(QSComponent qsComponent, @Nullable Bundle savedInstanceState) {
+        mRootView = qsComponent.getRootView();
 
-    @Override
-    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
-        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
-        mQSPanelController = qsFragmentComponent.getQSPanelController();
-        mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+        mCommandQueue.addCallback(this);
+
+        mQSPanelController = qsComponent.getQSPanelController();
+        mQuickQSPanelController = qsComponent.getQuickQSPanelController();
 
         mQSPanelController.init();
         mQuickQSPanelController.init();
 
-        mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
-                this);
-        bindFooterActionsView(view);
+        mQSFooterActionsViewModel = mFooterActionsViewModelFactory
+                .create(mListeningAndVisibilityLifecycleOwner);
+        bindFooterActionsView(mRootView);
         mFooterActionsController.init();
 
-        mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
+        mQSPanelScrollView = mRootView.findViewById(R.id.expanded_qs_scroll_view);
         mQSPanelScrollView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     updateQsBounds();
@@ -238,26 +227,26 @@
                     if (mScrollListener != null) {
                         mScrollListener.onQsPanelScrollChanged(scrollY);
                     }
-        });
-        mHeader = view.findViewById(R.id.header);
-        mFooter = qsFragmentComponent.getQSFooter();
+                });
+        mHeader = mRootView.findViewById(R.id.header);
+        mFooter = qsComponent.getQSFooter();
 
-        mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
+        mQSContainerImplController = qsComponent.getQSContainerImplController();
         mQSContainerImplController.init();
         mContainer = mQSContainerImplController.getView();
         mDumpManager.registerDumpable(mContainer.getClass().getSimpleName(), mContainer);
 
-        mQSAnimator = qsFragmentComponent.getQSAnimator();
-        mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
+        mQSAnimator = qsComponent.getQSAnimator();
+        mQSSquishinessController = qsComponent.getQSSquishinessController();
 
-        mQSCustomizerController = qsFragmentComponent.getQSCustomizerController();
+        mQSCustomizerController = qsComponent.getQSCustomizerController();
         mQSCustomizerController.init();
         mQSCustomizerController.setQs(this);
         if (savedInstanceState != null) {
             setQsVisible(savedInstanceState.getBoolean(EXTRA_VISIBLE));
             setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
             setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
-            setEditLocation(view);
+            setEditLocation(mRootView);
             mQSCustomizerController.restoreInstanceState(savedInstanceState);
             if (mQsExpanded) {
                 mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
@@ -265,7 +254,7 @@
         }
         mStatusBarStateController.addCallback(this);
         onStateChanged(mStatusBarStateController.getState());
-        view.addOnLayoutChangeListener(
+        mRootView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
                     if (sizeChanged) {
@@ -327,15 +316,12 @@
         mScrollListener = listener;
     }
 
-    @Override
     public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
         mDumpManager.registerDumpable(getClass().getSimpleName(), this);
     }
 
-    @Override
     public void onDestroy() {
-        super.onDestroy();
+        mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
         if (mListening) {
             setListening(false);
@@ -351,9 +337,7 @@
         mListeningAndVisibilityLifecycleOwner.destroy();
     }
 
-    @Override
     public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
         outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
         outState.putBoolean(EXTRA_LISTENING, mListening);
         outState.putBoolean(EXTRA_VISIBLE, mQsVisible);
@@ -394,9 +378,7 @@
         mPanelView = panelView;
     }
 
-    @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
         setEditLocation(getView());
         if (newConfig.getLayoutDirection() != mLayoutDirection) {
             mLayoutDirection = newConfig.getLayoutDirection();
@@ -452,9 +434,9 @@
         int state2BeforeAdjustment = state2;
         state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
 
-        mQsFragmentDisableFlagsLogger.logDisableFlagChange(
-                /* new= */ new DisableState(state1, state2BeforeAdjustment),
-                /* newAfterLocalModification= */ new DisableState(state1, state2)
+        mQsDisableFlagsLogger.logDisableFlagChange(
+                /* new= */ new DisableFlagsLogger.DisableState(state1, state2BeforeAdjustment),
+                /* newAfterLocalModification= */ new DisableFlagsLogger.DisableState(state1, state2)
         );
 
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
@@ -919,32 +901,6 @@
         getView().setY(-getQsMinExpansionHeight());
     }
 
-    private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn
-            = new ViewTreeObserver.OnPreDrawListener() {
-        @Override
-        public boolean onPreDraw() {
-            getView().getViewTreeObserver().removeOnPreDrawListener(this);
-            getView().animate()
-                    .translationY(0f)
-                    .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .setListener(mAnimateHeaderSlidingInListener)
-                    .start();
-            return true;
-        }
-    };
-
-    private final Animator.AnimatorListener mAnimateHeaderSlidingInListener
-            = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mHeaderAnimating = false;
-            updateQsState();
-            // Unset the listener, otherwise this may persist for another view property animation
-            getView().animate().setListener(null);
-        }
-    };
-
     @Override
     public void onUpcomingStateChanged(int upcomingState) {
         if (upcomingState == KEYGUARD) {
@@ -1030,6 +986,20 @@
         return "GONE";
     }
 
+    @Override
+    public View getView() {
+        return mRootView;
+    }
+
+    @Override
+    public Context getContext() {
+        return mRootView.getContext();
+    }
+
+    private Resources getResources() {
+        return getContext().getResources();
+    }
+
     /**
      * A {@link LifecycleOwner} whose state is driven by the current state of this fragment:
      *
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 9359958..6bbdc54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -19,7 +19,7 @@
 import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
 
 import android.view.MotionEvent;
 import android.view.View;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ef81674..60c92c0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,9 +43,6 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.animation.DisappearParameters;
 
-import kotlin.Unit;
-import kotlin.jvm.functions.Function1;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -54,7 +50,8 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import javax.inject.Named;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
 
 /**
  * Controller for QSPanel views.
@@ -135,7 +132,7 @@
             T view,
             QSHost host,
             QSCustomizerController qsCustomizerController,
-            @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
+            boolean usingMediaPlayer,
             MediaHost mediaHost,
             MetricsLogger metricsLogger,
             UiEventLogger uiEventLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 099d19d8..f278dce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,14 +17,13 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA;
+import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
 
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.MediaHost;
@@ -32,6 +31,7 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.leak.RotationUtils;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7888f4c..a103566 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -33,11 +33,11 @@
 import androidx.recyclerview.widget.DefaultItemAnimator;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.systemui.res.R;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSUtils;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.LightBarController;
 
 /**
@@ -135,8 +135,10 @@
             setVisibility(View.VISIBLE);
             long duration = mClipper.animateCircularClip(
                     mX, mY, true, new ExpandAnimatorListener(tileAdapter));
-            mQsContainerController.setCustomizerAnimating(true);
-            mQsContainerController.setCustomizerShowing(true, duration);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(true);
+                mQsContainerController.setCustomizerShowing(true, duration);
+            }
         }
     }
 
@@ -150,8 +152,10 @@
             mClipper.showBackground();
             isShown = true;
             setCustomizing(true);
-            mQsContainerController.setCustomizerAnimating(false);
-            mQsContainerController.setCustomizerShowing(true);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(false);
+                mQsContainerController.setCustomizerShowing(true);
+            }
         }
     }
 
@@ -169,8 +173,10 @@
             } else {
                 setVisibility(View.GONE);
             }
-            mQsContainerController.setCustomizerAnimating(animate);
-            mQsContainerController.setCustomizerShowing(false, duration);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(animate);
+                mQsContainerController.setCustomizerShowing(false, duration);
+            }
         }
     }
 
@@ -180,7 +186,9 @@
 
     void setCustomizing(boolean customizing) {
         mCustomizing = customizing;
-        mQs.notifyCustomizeChanged();
+        if (mQs != null) {
+            mQs.notifyCustomizeChanged();
+        }
     }
 
     public boolean isCustomizing() {
@@ -208,15 +216,21 @@
                 setCustomizing(true);
             }
             mOpening = false;
-            mQsContainerController.setCustomizerAnimating(false);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(false);
+            }
             mRecyclerView.setAdapter(mTileAdapter);
         }
 
         @Override
         public void onAnimationCancel(Animator animation) {
             mOpening = false;
-            mQs.notifyCustomizeChanged();
-            mQsContainerController.setCustomizerAnimating(false);
+            if (mQs != null) {
+                mQs.notifyCustomizeChanged();
+            }
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(false);
+            }
         }
     }
 
@@ -226,7 +240,9 @@
             if (!isShown) {
                 setVisibility(View.GONE);
             }
-            mQsContainerController.setCustomizerAnimating(false);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(false);
+            }
         }
 
         @Override
@@ -234,7 +250,9 @@
             if (!isShown) {
                 setVisibility(View.GONE);
             }
-            mQsContainerController.setCustomizerAnimating(false);
+            if (mQsContainerController != null) {
+                mQsContainerController.setCustomizerAnimating(false);
+            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index ce504b2..024e760 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -34,14 +34,14 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.res.R;
 import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSEditEvent;
-import com.android.systemui.qs.QSFragment;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -199,7 +199,7 @@
     }
 
     /** */
-    public void setQs(@Nullable QSFragment qsFragment) {
+    public void setQs(@Nullable QS qsFragment) {
         mView.setQs(qsFragment);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
new file mode 100644
index 0000000..f3413b80
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSComponent.kt
@@ -0,0 +1,40 @@
+package com.android.systemui.qs.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.QSAnimator
+import com.android.systemui.qs.QSContainerImplController
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSPanelController
+import com.android.systemui.qs.QSSquishinessController
+import com.android.systemui.qs.QuickQSPanelController
+import com.android.systemui.qs.customize.QSCustomizerController
+
+interface QSComponent {
+    /** Construct a [QSPanelController]. */
+    fun getQSPanelController(): QSPanelController
+
+    /** Construct a [QuickQSPanelController]. */
+    fun getQuickQSPanelController(): QuickQSPanelController
+
+    /** Construct a [QSAnimator]. */
+    fun getQSAnimator(): QSAnimator
+
+    /** Construct a [QSContainerImplController]. */
+    fun getQSContainerImplController(): QSContainerImplController
+
+    /** Construct a [QSFooter] */
+    fun getQSFooter(): QSFooter
+
+    /** Construct a [QSCustomizerController]. */
+    fun getQSCustomizerController(): QSCustomizerController
+
+    /** Construct a [QSSquishinessController]. */
+    fun getQSSquishinessController(): QSSquishinessController
+
+    /** Construct a [FooterActionsController]. */
+    fun getQSFooterActionController(): FooterActionsController
+
+    @RootView fun getRootView(): View
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
new file mode 100644
index 0000000..ba1aa62
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassComponent.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.dagger
+
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+@Subcomponent(modules = [QSFlexiglassModule::class])
+@QSScope
+interface QSFlexiglassComponent : QSComponent {
+
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(@RootView @BindsInstance rootView: View): QSFlexiglassComponent
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
new file mode 100644
index 0000000..36fac44
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlexiglassModule.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.dagger
+
+import android.content.Context
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_COLLAPSED_LANDSCAPE_MEDIA
+import com.android.systemui.qs.dagger.QSScopeModule.Companion.QS_USING_MEDIA_PLAYER
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+
+@Module(includes = [QSScopeModule::class])
+interface QSFlexiglassModule {
+
+    @Module
+    companion object {
+
+        /**  */
+        @Provides
+        @Named(QS_USING_MEDIA_PLAYER)
+        @JvmStatic
+        fun providesQSUsingMediaPlayer(context: Context?): Boolean {
+            return false
+        }
+
+        /**  */
+        @Provides
+        @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+        @JvmStatic
+        fun providesQSUsingCollapsedLandscapeMedia(context: Context): Boolean {
+            return false
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 594f4f8..327e858 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,53 +16,21 @@
 
 package com.android.systemui.qs.dagger;
 
-import com.android.systemui.qs.FooterActionsController;
-import com.android.systemui.qs.QSAnimator;
-import com.android.systemui.qs.QSContainerImplController;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanelController;
-import com.android.systemui.qs.QSSquishinessController;
-import com.android.systemui.qs.QuickQSPanelController;
-import com.android.systemui.qs.customize.QSCustomizerController;
+import com.android.systemui.qs.QSFragmentLegacy;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
- * Dagger Subcomponent for {@link QSFragment}.
+ * Dagger Subcomponent for {@link QSFragmentLegacy}.
  */
 @Subcomponent(modules = {QSFragmentModule.class})
 @QSScope
-public interface QSFragmentComponent {
+public interface QSFragmentComponent extends QSComponent {
 
     /** Factory for building a {@link QSFragmentComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        QSFragmentComponent create(@BindsInstance QSFragment qsFragment);
+        QSFragmentComponent create(@BindsInstance QSFragmentLegacy qsFragment);
     }
-
-    /** Construct a {@link QSPanelController}. */
-    QSPanelController getQSPanelController();
-
-    /** Construct a {@link QuickQSPanelController}. */
-    QuickQSPanelController getQuickQSPanelController();
-
-    /** Construct a {@link QSAnimator}. */
-    QSAnimator getQSAnimator();
-
-    /** Construct a {@link QSContainerImplController}. */
-    QSContainerImplController getQSContainerImplController();
-
-    /** Construct a {@link QSFooter} */
-    QSFooter getQSFooter();
-
-    /** Construct a {@link QSCustomizerController}. */
-    QSCustomizerController getQSCustomizerController();
-
-    /** Construct a {@link QSSquishinessController}. */
-    QSSquishinessController getQSSquishinessController();
-
-    /** Construct a {@link FooterActionsController}. */
-    FooterActionsController getQSFooterActionController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index bcd9803..0c9c24d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -20,21 +20,11 @@
 import static com.android.systemui.util.Utils.useQsMediaPlayer;
 
 import android.content.Context;
-import android.view.LayoutInflater;
 import android.view.View;
 
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSContainerImpl;
-import com.android.systemui.qs.QSFooter;
-import com.android.systemui.qs.QSFooterView;
-import com.android.systemui.qs.QSFooterViewController;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.QuickQSPanel;
-import com.android.systemui.qs.QuickStatusBarHeader;
-import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.QSFragmentLegacy;
 
 import javax.inject.Named;
 
@@ -45,93 +35,31 @@
 /**
  * Dagger Module for {@link QSFragmentComponent}.
  */
-@Module
-public interface QSFragmentModule {
-    String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
-    String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media";
+@Module(includes = {QSScopeModule.class})
+public  interface QSFragmentModule {
 
-    /**
-     * Provide a context themed using the QS theme
-     */
-    @Provides
-    @QSThemedContext
-    static Context provideThemedContext(@RootView View view) {
-        return view.getContext();
-    }
-
-    /** */
-    @Provides
-    @QSThemedContext
-    static LayoutInflater provideThemedLayoutInflater(@QSThemedContext Context context) {
-        return LayoutInflater.from(context);
-    }
-
-    /** */
     @Provides
     @RootView
-    static View provideRootView(QSFragment qsFragment) {
+    static View provideRootView(QSFragmentLegacy qsFragment) {
         return qsFragment.getView();
     }
 
     /** */
-    @Provides
-    static QSPanel provideQSPanel(@RootView View view) {
-        return view.findViewById(R.id.quick_settings_panel);
-    }
-
-    /** */
-    @Provides
-    static QSContainerImpl providesQSContainerImpl(@RootView View view) {
-        return view.findViewById(R.id.quick_settings_container);
-    }
-
-    /** */
     @Binds
-    QS bindQS(QSFragment qsFragment);
+    QS bindQS(QSFragmentLegacy qsFragment);
 
     /** */
     @Provides
-    static QuickStatusBarHeader providesQuickStatusBarHeader(@RootView View view) {
-        return view.findViewById(R.id.header);
-    }
-
-    /** */
-    @Provides
-    static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) {
-        return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
-    }
-
-    /** */
-    @Provides
-    static QSFooterView providesQSFooterView(@RootView View view) {
-        return view.findViewById(R.id.qs_footer);
-    }
-
-    /** */
-    @Provides
-    @QSScope
-    static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
-        qsFooterViewController.init();
-        return qsFooterViewController;
-    }
-
-    /** */
-    @Provides
-    @QSScope
-    static QSCustomizer providesQSCutomizer(@RootView View view) {
-        return view.findViewById(R.id.qs_customize);
-    }
-
-    /** */
-    @Provides
-    @Named(QS_USING_MEDIA_PLAYER)
+    @Named(QSScopeModule.QS_USING_MEDIA_PLAYER)
     static boolean providesQSUsingMediaPlayer(Context context) {
         return useQsMediaPlayer(context);
     }
 
+
+
     /** */
     @Provides
-    @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+    @Named(QSScopeModule.QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
     static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
         return useCollapsedMediaInLandscape(context.getResources());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 03de3a0..92490e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -52,7 +52,7 @@
 /**
  * Module for QS dependencies
  */
-@Module(subcomponents = {QSFragmentComponent.class},
+@Module(subcomponents = {QSFragmentComponent.class, QSFlexiglassComponent.class},
         includes = {
                 MediaModule.class,
                 QSExternalModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
new file mode 100644
index 0000000..e68ec4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScopeModule.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.qs.dagger
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import com.android.systemui.dagger.qualifiers.RootView
+import com.android.systemui.qs.QSContainerImpl
+import com.android.systemui.qs.QSFooter
+import com.android.systemui.qs.QSFooterView
+import com.android.systemui.qs.QSFooterViewController
+import com.android.systemui.qs.QSPanel
+import com.android.systemui.qs.QuickQSPanel
+import com.android.systemui.qs.QuickStatusBarHeader
+import com.android.systemui.qs.customize.QSCustomizer
+import com.android.systemui.res.R
+import dagger.Module
+import dagger.Provides
+
+@Module
+interface QSScopeModule {
+    companion object {
+        const val QS_USING_MEDIA_PLAYER = "qs_using_media_player"
+        const val QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media"
+
+        @Provides
+        @QSThemedContext
+        @JvmStatic
+        fun provideThemedContext(@RootView view: View): Context {
+            return view.context
+        }
+
+        /**  */
+        @Provides
+        @QSThemedContext
+        @JvmStatic
+        fun provideThemedLayoutInflater(@QSThemedContext context: Context): LayoutInflater {
+            return LayoutInflater.from(context)
+        }
+
+        /**  */
+        @Provides
+        @JvmStatic
+        fun provideQSPanel(@RootView view: View): QSPanel {
+            return view.requireViewById<QSPanel>(R.id.quick_settings_panel)
+        }
+
+        /**  */
+        @Provides
+        @JvmStatic
+        fun providesQSContainerImpl(@RootView view: View): QSContainerImpl {
+            return view.requireViewById<QSContainerImpl>(R.id.quick_settings_container)
+        }
+
+        /**  */
+        @Provides
+        @JvmStatic
+        fun providesQuickStatusBarHeader(@RootView view: View): QuickStatusBarHeader {
+            return view.requireViewById<QuickStatusBarHeader>(R.id.header)
+        }
+
+        /**  */
+        @Provides
+        @JvmStatic
+        fun providesQuickQSPanel(quickStatusBarHeader: QuickStatusBarHeader): QuickQSPanel {
+            return quickStatusBarHeader.requireViewById<QuickQSPanel>(R.id.quick_qs_panel)
+        }
+
+        /**  */
+        @Provides
+        @JvmStatic
+        fun providesQSFooterView(@RootView view: View): QSFooterView {
+            return view.requireViewById<QSFooterView>(R.id.qs_footer)
+        }
+
+        /**  */
+        @Provides
+        @QSScope
+        @JvmStatic
+        fun providesQSFooter(qsFooterViewController: QSFooterViewController): QSFooter {
+            qsFooterViewController.init()
+            return qsFooterViewController
+        }
+
+        /**  */
+        @Provides
+        @QSScope
+        @JvmStatic
+        fun providesQSCutomizer(@RootView view: View): QSCustomizer {
+            return view.requireViewById<QSCustomizer>(R.id.qs_customize)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
index b394a07..8b2c3de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt
@@ -72,7 +72,7 @@
      * Show the device monitoring dialog, expanded from [expandable] if it's not null.
      *
      * Important: [quickSettingsContext] *must* be the [Context] associated to the
-     * [Quick Settings fragment][com.android.systemui.qs.QSFragment].
+     * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy].
      */
     fun showDeviceMonitoringDialog(quickSettingsContext: Context, expandable: Expandable?)
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 769cb1f..64fa33c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -23,7 +23,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import com.android.settingslib.Utils
-import com.android.systemui.res.R
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
@@ -34,6 +33,7 @@
 import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
 import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
 import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
+import com.android.systemui.res.R
 import com.android.systemui.util.icuMessageFormat
 import javax.inject.Inject
 import javax.inject.Named
@@ -43,7 +43,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
@@ -201,8 +200,8 @@
      * will suspend indefinitely and will need to be cancelled to stop observing.
      *
      * Important: [quickSettingsContext] must be the [Context] associated to the
-     * [Quick Settings fragment][com.android.systemui.qs.QSFragment], and the call to this function
-     * must be cancelled when that fragment is destroyed.
+     * [Quick Settings fragment][com.android.systemui.qs.QSFragmentLegacy], and the call to this
+     * function must be cancelled when that fragment is destroyed.
      */
     suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) {
         footerActionsInteractor.deviceMonitoringDialogRequests.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index a4600fb..21aaa94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -20,8 +20,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.pipeline.data.repository.DefaultTilesQSHostRepository
+import com.android.systemui.qs.pipeline.data.repository.DefaultTilesRepository
 import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository
 import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredBroadcastRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
 import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
 import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -42,6 +46,11 @@
     abstract fun provideTileSpecRepository(impl: TileSpecSettingsRepository): TileSpecRepository
 
     @Binds
+    abstract fun provideDefaultTilesRepository(
+        impl: DefaultTilesQSHostRepository
+    ): DefaultTilesRepository
+
+    @Binds
     abstract fun bindCurrentTilesInteractor(
         impl: CurrentTilesInteractorImpl
     ): CurrentTilesInteractor
@@ -56,6 +65,11 @@
     @ClassKey(QSPipelineCoreStartable::class)
     abstract fun provideCoreStartable(startable: QSPipelineCoreStartable): CoreStartable
 
+    @Binds
+    abstract fun provideQSSettingsRestoredRepository(
+        impl: QSSettingsRestoredBroadcastRepository
+    ): QSSettingsRestoredRepository
+
     companion object {
         /**
          * Provides a logging buffer for all logs related to the new Quick Settings pipeline to log
@@ -67,5 +81,12 @@
         fun provideQSTileListLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create(QSPipelineLogger.TILE_LIST_TAG, maxSize = 700, systrace = false)
         }
+
+        @Provides
+        @SysUISingleton
+        @QSRestoreLog
+        fun providesQSRestoreLogBuffer(factory: LogBufferFactory): LogBuffer {
+            return factory.create(QSPipelineLogger.RESTORE_TAG, maxSize = 50, systrace = false)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
new file mode 100644
index 0000000..c964929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSRestoreLog.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.qs.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/** A [LogBuffer] for the QS pipeline to track restore of associated settings. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class QSRestoreLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
new file mode 100644
index 0000000..d962632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/model/RestoreData.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.qs.pipeline.data.model
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+/** Data restored from Quick Settings as part of Backup & Restore. */
+data class RestoreData(
+    val restoredTiles: List<TileSpec>,
+    val restoredAutoAddedTiles: Set<TileSpec>,
+    val userId: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
index 43a16b6..7998dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/AutoAddRepository.kt
@@ -16,28 +16,19 @@
 
 package com.android.systemui.qs.pipeline.data.repository
 
-import android.database.ContentObserver
-import android.provider.Settings
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import android.util.SparseArray
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.SecureSettings
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
 
 /** Repository to track what QS tiles have been auto-added */
 interface AutoAddRepository {
 
     /** Flow of tiles that have been auto-added */
-    fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
+    suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>>
 
     /** Mark a tile as having been auto-added */
     suspend fun markTileAdded(userId: Int, spec: TileSpec)
@@ -47,89 +38,39 @@
      * multiple times.
      */
     suspend fun unmarkTileAdded(userId: Int, spec: TileSpec)
+
+    suspend fun reconcileRestore(restoreData: RestoreData)
 }
 
 /**
- * Implementation that tracks the auto-added tiles stored in [Settings.Secure.QS_AUTO_ADDED_TILES].
+ * Implementation of [AutoAddRepository] that delegates to an instance of [UserAutoAddRepository]
+ * for each user.
  */
 @SysUISingleton
 class AutoAddSettingRepository
 @Inject
-constructor(
-    private val secureSettings: SecureSettings,
-    @Background private val bgDispatcher: CoroutineDispatcher,
-) : AutoAddRepository {
-    override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
-        return conflatedCallbackFlow {
-                val observer =
-                    object : ContentObserver(null) {
-                        override fun onChange(selfChange: Boolean) {
-                            trySend(Unit)
-                        }
-                    }
+constructor(private val userAutoAddRepositoryFactory: UserAutoAddRepository.Factory) :
+    AutoAddRepository {
 
-                secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+    private val userAutoAddRepositories = SparseArray<UserAutoAddRepository>()
 
-                awaitClose { secureSettings.unregisterContentObserver(observer) }
-            }
-            .onStart { emit(Unit) }
-            .map { secureSettings.getStringForUser(SETTING, userId) ?: "" }
-            .distinctUntilChanged()
-            .map {
-                it.split(DELIMITER).map(TileSpec::create).filter { it !is TileSpec.Invalid }.toSet()
-            }
-            .flowOn(bgDispatcher)
+    override suspend fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+        if (userId !in userAutoAddRepositories) {
+            val repository = userAutoAddRepositoryFactory.create(userId)
+            userAutoAddRepositories.put(userId, repository)
+        }
+        return userAutoAddRepositories.get(userId).autoAdded()
     }
 
     override suspend fun markTileAdded(userId: Int, spec: TileSpec) {
-        if (spec is TileSpec.Invalid) {
-            return
-        }
-        val added = load(userId).toMutableSet()
-        if (added.add(spec)) {
-            store(userId, added)
-        }
+        userAutoAddRepositories.get(userId)?.markTileAdded(spec)
     }
 
     override suspend fun unmarkTileAdded(userId: Int, spec: TileSpec) {
-        if (spec is TileSpec.Invalid) {
-            return
-        }
-        val added = load(userId).toMutableSet()
-        if (added.remove(spec)) {
-            store(userId, added)
-        }
+        userAutoAddRepositories.get(userId)?.unmarkTileAdded(spec)
     }
 
-    private suspend fun store(userId: Int, tiles: Set<TileSpec>) {
-        val toStore =
-            tiles
-                .filter { it !is TileSpec.Invalid }
-                .joinToString(DELIMITER, transform = TileSpec::spec)
-        withContext(bgDispatcher) {
-            secureSettings.putStringForUser(
-                SETTING,
-                toStore,
-                null,
-                false,
-                userId,
-                true,
-            )
-        }
-    }
-
-    private suspend fun load(userId: Int): Set<TileSpec> {
-        return withContext(bgDispatcher) {
-            (secureSettings.getStringForUser(SETTING, userId) ?: "")
-                .split(",")
-                .map(TileSpec::create)
-                .filter { it !is TileSpec.Invalid }
-                .toSet()
-        }
-    }
-
-    companion object {
-        private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
-        private const val DELIMITER = ","
+    override suspend fun reconcileRestore(restoreData: RestoreData) {
+        userAutoAddRepositories.get(restoreData.userId)?.reconcileRestore(restoreData)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
new file mode 100644
index 0000000..fe0a69b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/DefaultTilesRepository.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+interface DefaultTilesRepository {
+    val defaultTiles: List<TileSpec>
+}
+
+@SysUISingleton
+class DefaultTilesQSHostRepository
+@Inject
+constructor(
+    @Main private val resources: Resources,
+) : DefaultTilesRepository {
+    override val defaultTiles: List<TileSpec>
+        get() =
+            QSHost.getDefaultSpecs(resources).map(TileSpec::create).filter {
+                it != TileSpec.Invalid
+            }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
new file mode 100644
index 0000000..6cee116
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredRepository.kt
@@ -0,0 +1,122 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import android.provider.Settings
+import android.util.Log
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter.toTilesList
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.shareIn
+
+/** Provides restored data (from Backup and Restore) for Quick Settings pipeline */
+interface QSSettingsRestoredRepository {
+    val restoreData: Flow<RestoreData>
+}
+
+@SysUISingleton
+class QSSettingsRestoredBroadcastRepository
+@Inject
+constructor(
+    broadcastDispatcher: BroadcastDispatcher,
+    logger: QSPipelineLogger,
+    @Application private val scope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) : QSSettingsRestoredRepository {
+
+    override val restoreData =
+        flow {
+                val firstIntent = mutableMapOf<Int, Intent>()
+                broadcastDispatcher
+                    .broadcastFlow(INTENT_FILTER, UserHandle.ALL) { intent, receiver ->
+                        intent to receiver.sendingUserId
+                    }
+                    .filter { it.first.isCorrectSetting() }
+                    .collect { (intent, user) ->
+                        if (user !in firstIntent) {
+                            firstIntent[user] = intent
+                        } else {
+                            val firstRestored = firstIntent.remove(user)!!
+                            emit(processIntents(user, firstRestored, intent))
+                        }
+                    }
+            }
+            .catch { Log.e(TAG, "Error parsing broadcast", it) }
+            .flowOn(backgroundDispatcher)
+            .buffer(BUFFER_CAPACITY)
+            .shareIn(scope, SharingStarted.Eagerly)
+            .onEach(logger::logSettingsRestored)
+
+    private fun processIntents(user: Int, intent1: Intent, intent2: Intent): RestoreData {
+        intent1.validateIntent()
+        intent2.validateIntent()
+        val setting1 = intent1.getStringExtra(Intent.EXTRA_SETTING_NAME)
+        val setting2 = intent2.getStringExtra(Intent.EXTRA_SETTING_NAME)
+        val (tiles, autoAdd) =
+            if (setting1 == TILES_SETTING && setting2 == AUTO_ADD_SETTING) {
+                intent1 to intent2
+            } else if (setting1 == AUTO_ADD_SETTING && setting2 == TILES_SETTING) {
+                intent2 to intent1
+            } else {
+                throw IllegalStateException("Wrong intents ($intent1, $intent2)")
+            }
+
+        return RestoreData(
+            (tiles.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesList(),
+            (autoAdd.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE) ?: "").toTilesSet(),
+            user,
+        )
+    }
+
+    private companion object {
+        private const val TAG = "QSSettingsRestoredBroadcastRepository"
+        // This capacity is the number of restore data that we will keep buffered in the shared
+        // flow. It is unlikely that at any given time there would be this many restores being
+        // processed by consumers, but just in case that a couple of users are restored at the
+        // same time and they need to be replayed for the consumers of the flow.
+        private const val BUFFER_CAPACITY = 10
+
+        private val INTENT_FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
+        private const val TILES_SETTING = Settings.Secure.QS_TILES
+        private const val AUTO_ADD_SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+        private val requiredExtras =
+            listOf(
+                Intent.EXTRA_SETTING_NAME,
+                Intent.EXTRA_SETTING_PREVIOUS_VALUE,
+                Intent.EXTRA_SETTING_NEW_VALUE,
+            )
+
+        private fun Intent.isCorrectSetting(): Boolean {
+            val setting = getStringExtra(Intent.EXTRA_SETTING_NAME)
+            return setting == TILES_SETTING || setting == AUTO_ADD_SETTING
+        }
+
+        private fun Intent.validateIntent() {
+            requiredExtras.forEach { extra ->
+                if (!hasExtra(extra)) {
+                    throw IllegalStateException("$this doesn't have $extra")
+                }
+            }
+        }
+
+        private fun String.toTilesList() = toTilesList(this)
+
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 47c99f2..00ea0b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -18,34 +18,19 @@
 
 import android.annotation.UserIdInt
 import android.content.res.Resources
-import android.database.ContentObserver
-import android.provider.Settings
 import android.util.SparseArray
 import com.android.systemui.res.R
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.retail.data.repository.RetailModeRepository
-import com.android.systemui.util.settings.SecureSettings
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.withContext
 
 /** Repository that tracks the current tiles. */
 interface TileSpecRepository {
@@ -55,7 +40,7 @@
      *
      * Tiles will never be [TileSpec.Invalid] in the list and it will never be empty.
      */
-    fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
+    suspend fun tilesSpecs(@UserIdInt userId: Int): Flow<List<TileSpec>>
 
     /**
      * Adds a [tile] for a given [userId] at [position]. Using [POSITION_AT_END] will add the tile
@@ -81,6 +66,8 @@
      */
     suspend fun setTiles(@UserIdInt userId: Int, tiles: List<TileSpec>)
 
+    suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>)
+
     companion object {
         /** Position to indicate the end of the list */
         const val POSITION_AT_END = -1
@@ -88,28 +75,23 @@
 }
 
 /**
- * Implementation of [TileSpecRepository] that persist the values of tiles in
- * [Settings.Secure.QS_TILES].
- *
- * All operations against [Settings] will be performed in a background thread.
+ * Implementation of [TileSpecRepository] that delegates to an instance of [UserTileSpecRepository]
+ * for each user.
  *
  * If the device is in retail mode, the tiles are fixed to the value of
  * [R.string.quick_settings_tiles_retail_mode].
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class TileSpecSettingsRepository
 @Inject
 constructor(
-    private val secureSettings: SecureSettings,
     @Main private val resources: Resources,
     private val logger: QSPipelineLogger,
     private val retailModeRepository: RetailModeRepository,
-    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val userTileSpecRepositoryFactory: UserTileSpecRepository.Factory,
 ) : TileSpecRepository {
 
-    private val mutex = Mutex()
-    private val tileSpecsPerUser = SparseArray<List<TileSpec>>()
-
     private val retailModeTiles by lazy {
         resources
             .getString(R.string.quick_settings_tiles_retail_mode)
@@ -118,123 +100,59 @@
             .filter { it !is TileSpec.Invalid }
     }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
-    override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+    private val userTileRepositories = SparseArray<UserTileSpecRepository>()
+
+    override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+        if (userId !in userTileRepositories) {
+            val userTileRepository = userTileSpecRepositoryFactory.create(userId)
+            userTileRepositories.put(userId, userTileRepository)
+        }
+        val realTiles = userTileRepositories.get(userId).tiles()
+
         return retailModeRepository.retailMode.flatMapLatest { inRetailMode ->
             if (inRetailMode) {
                 logger.logUsingRetailTiles()
                 flowOf(retailModeTiles)
             } else {
-                settingsTiles(userId)
+                realTiles
             }
         }
     }
 
-    private fun settingsTiles(userId: Int): Flow<List<TileSpec>> {
-        return conflatedCallbackFlow {
-                val observer =
-                    object : ContentObserver(null) {
-                        override fun onChange(selfChange: Boolean) {
-                            trySend(Unit)
-                        }
-                    }
-
-                secureSettings.registerContentObserverForUser(SETTING, observer, userId)
-
-                awaitClose { secureSettings.unregisterContentObserver(observer) }
-            }
-            .onStart { emit(Unit) }
-            .map { loadTiles(userId) }
-            .onEach { logger.logTilesChangedInSettings(it, userId) }
-            .distinctUntilChanged()
-            .map { parseTileSpecs(it, userId).also { storeTiles(userId, it) } }
-            .distinctUntilChanged()
-            .onEach { mutex.withLock { tileSpecsPerUser.put(userId, it) } }
-            .flowOn(backgroundDispatcher)
-    }
-
-    override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) =
-        mutex.withLock {
-            if (tile == TileSpec.Invalid) {
-                return
-            }
-            val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
-            if (tile !in tilesList) {
-                if (position < 0 || position >= tilesList.size) {
-                    tilesList.add(tile)
-                } else {
-                    tilesList.add(position, tile)
-                }
-                storeTiles(userId, tilesList)
-                tileSpecsPerUser.put(userId, tilesList)
-            }
-        }
-
-    override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) =
-        mutex.withLock {
-            if (tiles.all { it == TileSpec.Invalid }) {
-                return
-            }
-            val tilesList = tileSpecsPerUser.get(userId, emptyList()).toMutableList()
-            if (tilesList.removeAll(tiles)) {
-                storeTiles(userId, tilesList.toList())
-                tileSpecsPerUser.put(userId, tilesList)
-            }
-        }
-
-    override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) =
-        mutex.withLock {
-            val filtered = tiles.filter { it != TileSpec.Invalid }
-            if (filtered.isNotEmpty()) {
-                storeTiles(userId, filtered)
-                tileSpecsPerUser.put(userId, tiles)
-            }
-        }
-
-    private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+    override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) {
         if (retailModeRepository.inRetailMode) {
-            // No storing tiles when in retail mode
             return
         }
-        val toStore =
-            tiles
-                .filter { it !is TileSpec.Invalid }
-                .joinToString(DELIMITER, transform = TileSpec::spec)
-        withContext(backgroundDispatcher) {
-            secureSettings.putStringForUser(
-                SETTING,
-                toStore,
-                null,
-                false,
-                forUser,
-                true,
-            )
+        if (tile is TileSpec.Invalid) {
+            return
         }
+        userTileRepositories.get(userId)?.addTile(tile, position)
     }
 
-    private suspend fun loadTiles(userId: Int): String {
-        return withContext(backgroundDispatcher) {
-            secureSettings.getStringForUser(SETTING, userId) ?: ""
+    override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) {
+        if (retailModeRepository.inRetailMode) {
+            return
         }
+        userTileRepositories.get(userId)?.removeTiles(tiles)
     }
 
-    private fun parseTileSpecs(tilesFromSettings: String, user: Int): List<TileSpec> {
-        val fromSettings =
-            tilesFromSettings.split(DELIMITER).map(TileSpec::create).filter {
-                it != TileSpec.Invalid
-            }
-        return if (fromSettings.isNotEmpty()) {
-            fromSettings.also { logger.logParsedTiles(it, false, user) }
-        } else {
-            QSHost.getDefaultSpecs(resources)
-                .map(TileSpec::create)
-                .filter { it != TileSpec.Invalid }
-                .also { logger.logParsedTiles(it, true, user) }
+    override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) {
+        if (retailModeRepository.inRetailMode) {
+            return
         }
+        userTileRepositories.get(userId)?.setTiles(tiles)
+    }
+
+    override suspend fun reconcileRestore(
+        restoreData: RestoreData,
+        currentAutoAdded: Set<TileSpec>
+    ) {
+        userTileRepositories
+            .get(restoreData.userId)
+            ?.reconcileRestore(restoreData, currentAutoAdded)
     }
 
     companion object {
-        private const val SETTING = Settings.Secure.QS_TILES
-        private const val DELIMITER = ","
+        private const val DELIMITER = TilesSettingConverter.DELIMITER
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
new file mode 100644
index 0000000..bb55fcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverter.kt
@@ -0,0 +1,18 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+object TilesSettingConverter {
+
+    const val DELIMITER = ","
+
+    fun toTilesList(commaSeparatedTiles: String) =
+        commaSeparatedTiles.split(DELIMITER).map(TileSpec::create).filter { it != TileSpec.Invalid }
+
+    fun toTilesSet(commaSeparatedTiles: String) =
+        commaSeparatedTiles
+            .split(DELIMITER)
+            .map(TileSpec::create)
+            .filter { it != TileSpec.Invalid }
+            .toSet()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
new file mode 100644
index 0000000..d452241
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -0,0 +1,186 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [AutoAddRepository]. It provides a similar interface as
+ * [AutoAddRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserAutoAddRepository
+@AssistedInject
+constructor(
+    @Assisted private val userId: Int,
+    private val secureSettings: SecureSettings,
+    private val logger: QSPipelineLogger,
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+) {
+
+    private val changeEvents = MutableSharedFlow<ChangeAction>(
+        extraBufferCapacity = CHANGES_BUFFER_SIZE
+    )
+
+    private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
+
+    suspend fun autoAdded(): StateFlow<Set<TileSpec>> {
+        if (!::_autoAdded.isInitialized) {
+            _autoAdded =
+                changeEvents
+                    .scan(load().also { logger.logAutoAddTilesParsed(userId, it) }) {
+                        current,
+                        change ->
+                        change.apply(current).also {
+                            if (change is RestoreTiles) {
+                                logger.logAutoAddTilesRestoredReconciled(userId, it)
+                            }
+                        }
+                    }
+                    .flowOn(bgDispatcher)
+                    .stateIn(applicationScope)
+                    .also { startFlowCollections(it) }
+        }
+        return _autoAdded
+    }
+
+    private fun startFlowCollections(autoAdded: StateFlow<Set<TileSpec>>) {
+        applicationScope.launch(bgDispatcher) {
+            launch { autoAdded.collect { store(it) } }
+            launch {
+                // As Settings is not the source of truth, once we started tracking tiles for a
+                // user, we don't want anyone to change the underlying setting. Therefore, if there
+                // are any changes that don't match with the source of truth (this class), we
+                // overwrite them with the current value.
+                ConflatedCallbackFlow.conflatedCallbackFlow {
+                        val observer =
+                            object : ContentObserver(null) {
+                                override fun onChange(selfChange: Boolean) {
+                                    trySend(Unit)
+                                }
+                            }
+                        secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+                        awaitClose { secureSettings.unregisterContentObserver(observer) }
+                    }
+                    .map { load() }
+                    .flowOn(bgDispatcher)
+                    .collect { setting ->
+                        val current = autoAdded.value
+                        if (setting != current) {
+                            store(current)
+                        }
+                    }
+            }
+        }
+    }
+
+    suspend fun markTileAdded(spec: TileSpec) {
+        if (spec is TileSpec.Invalid) {
+            return
+        }
+        changeEvents.emit(MarkTile(spec))
+    }
+
+    suspend fun unmarkTileAdded(spec: TileSpec) {
+        if (spec is TileSpec.Invalid) {
+            return
+        }
+        changeEvents.emit(UnmarkTile(spec))
+    }
+
+    private suspend fun store(tiles: Set<TileSpec>) {
+        val toStore =
+            tiles
+                .filter { it !is TileSpec.Invalid }
+                .joinToString(DELIMITER, transform = TileSpec::spec)
+        withContext(bgDispatcher) {
+            secureSettings.putStringForUser(
+                SETTING,
+                toStore,
+                null,
+                false,
+                userId,
+                true,
+            )
+        }
+    }
+
+    private suspend fun load(): Set<TileSpec> {
+        return withContext(bgDispatcher) {
+            (secureSettings.getStringForUser(SETTING, userId) ?: "").toTilesSet()
+        }
+    }
+
+    suspend fun reconcileRestore(restoreData: RestoreData) {
+        changeEvents.emit(RestoreTiles(restoreData))
+    }
+
+    private sealed interface ChangeAction {
+        fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec>
+    }
+
+    private data class MarkTile(
+        val tileSpec: TileSpec,
+    ) : ChangeAction {
+        override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+            return currentAutoAdded.toMutableSet().apply { add(tileSpec) }
+        }
+    }
+
+    private data class UnmarkTile(
+        val tileSpec: TileSpec,
+    ) : ChangeAction {
+        override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+            return currentAutoAdded.toMutableSet().apply { remove(tileSpec) }
+        }
+    }
+
+    private data class RestoreTiles(
+        val restoredData: RestoreData,
+    ) : ChangeAction {
+        override fun apply(currentAutoAdded: Set<TileSpec>): Set<TileSpec> {
+            return currentAutoAdded + restoredData.restoredAutoAddedTiles
+        }
+    }
+
+    companion object {
+        private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+        private const val DELIMITER = ","
+        // We want a small buffer in case multiple changes come in at the same time (sometimes
+        // happens in first start. This should be enough to not lose changes.
+        private const val CHANGES_BUFFER_SIZE = 10
+
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(userId: Int): UserAutoAddRepository
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
new file mode 100644
index 0000000..152fd0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -0,0 +1,252 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.annotation.UserIdInt
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.SecureSettings
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Single user version of [TileSpecRepository]. It provides a similar interface as
+ * [TileSpecRepository], but focusing solely on the user it was created for.
+ *
+ * This is the source of truth for that user's tiles, after the user has been started. Persisting
+ * all the changes to [Settings]. Changes in [Settings] that disagree with this repository will be
+ * reverted
+ *
+ * All operations against [Settings] will be performed in a background thread.
+ */
+class UserTileSpecRepository
+@AssistedInject
+constructor(
+    @Assisted private val userId: Int,
+    private val defaultTilesRepository: DefaultTilesRepository,
+    private val secureSettings: SecureSettings,
+    private val logger: QSPipelineLogger,
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+    private val defaultTiles: List<TileSpec>
+        get() = defaultTilesRepository.defaultTiles
+
+    private val changeEvents = MutableSharedFlow<ChangeAction>(
+        extraBufferCapacity = CHANGES_BUFFER_SIZE
+    )
+
+    private lateinit var _tiles: StateFlow<List<TileSpec>>
+
+    suspend fun tiles(): Flow<List<TileSpec>> {
+        if (!::_tiles.isInitialized) {
+            _tiles =
+                changeEvents
+                    .scan(loadTilesFromSettingsAndParse(userId)) { current, change ->
+                        change.apply(current).also {
+                            if (current != it) {
+                                if (change is RestoreTiles) {
+                                    logger.logTilesRestoredAndReconciled(current, it, userId)
+                                } else {
+                                    logger.logProcessTileChange(change, it, userId)
+                                }
+                            }
+                        }
+                    }
+                    .flowOn(backgroundDispatcher)
+                    .stateIn(applicationScope)
+                    .also { startFlowCollections(it) }
+        }
+        return _tiles
+    }
+
+    private fun startFlowCollections(tiles: StateFlow<List<TileSpec>>) {
+        applicationScope.launch(backgroundDispatcher) {
+            launch { tiles.collect { storeTiles(userId, it) } }
+            launch {
+                // As Settings is not the source of truth, once we started tracking tiles for a
+                // user, we don't want anyone to change the underlying setting. Therefore, if there
+                // are any changes that don't match with the source of truth (this class), we
+                // overwrite them with the current value.
+                ConflatedCallbackFlow.conflatedCallbackFlow {
+                        val observer =
+                            object : ContentObserver(null) {
+                                override fun onChange(selfChange: Boolean) {
+                                    trySend(Unit)
+                                }
+                            }
+                        secureSettings.registerContentObserverForUser(SETTING, observer, userId)
+                        awaitClose { secureSettings.unregisterContentObserver(observer) }
+                    }
+                    .map { loadTilesFromSettings(userId) }
+                    .flowOn(backgroundDispatcher)
+                    .collect { setting ->
+                        val current = tiles.value
+                        if (setting != current) {
+                            storeTiles(userId, current)
+                        }
+                    }
+            }
+        }
+    }
+
+    private suspend fun storeTiles(@UserIdInt forUser: Int, tiles: List<TileSpec>) {
+        val toStore =
+            tiles
+                .filter { it !is TileSpec.Invalid }
+                .joinToString(DELIMITER, transform = TileSpec::spec)
+        withContext(backgroundDispatcher) {
+            secureSettings.putStringForUser(
+                SETTING,
+                toStore,
+                null,
+                false,
+                forUser,
+                true,
+            )
+        }
+    }
+
+    suspend fun addTile(tile: TileSpec, position: Int = TileSpecRepository.POSITION_AT_END) {
+        if (tile is TileSpec.Invalid) {
+            return
+        }
+        changeEvents.emit(AddTile(tile, position))
+    }
+
+    suspend fun removeTiles(tiles: Collection<TileSpec>) {
+        changeEvents.emit(RemoveTiles(tiles))
+    }
+
+    suspend fun setTiles(tiles: List<TileSpec>) {
+        changeEvents.emit(ChangeTiles(tiles))
+    }
+
+    private fun parseTileSpecs(fromSettings: List<TileSpec>, user: Int): List<TileSpec> {
+        return if (fromSettings.isNotEmpty()) {
+            fromSettings.also { logger.logParsedTiles(it, false, user) }
+        } else {
+            defaultTiles.also { logger.logParsedTiles(it, true, user) }
+        }
+    }
+
+    private suspend fun loadTilesFromSettingsAndParse(userId: Int): List<TileSpec> {
+        return parseTileSpecs(loadTilesFromSettings(userId), userId)
+    }
+
+    private suspend fun loadTilesFromSettings(userId: Int): List<TileSpec> {
+        return withContext(backgroundDispatcher) {
+                secureSettings.getStringForUser(SETTING, userId) ?: ""
+            }
+            .toTilesList()
+    }
+
+    suspend fun reconcileRestore(restoreData: RestoreData, currentAutoAdded: Set<TileSpec>) {
+        changeEvents.emit(RestoreTiles(restoreData, currentAutoAdded))
+    }
+
+    sealed interface ChangeAction {
+        fun apply(currentTiles: List<TileSpec>): List<TileSpec>
+    }
+
+    private data class AddTile(
+        val tileSpec: TileSpec,
+        val position: Int = TileSpecRepository.POSITION_AT_END
+    ) : ChangeAction {
+        override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+            val tilesList = currentTiles.toMutableList()
+            if (tileSpec !in tilesList) {
+                if (position < 0 || position >= tilesList.size) {
+                    tilesList.add(tileSpec)
+                } else {
+                    tilesList.add(position, tileSpec)
+                }
+            }
+            return tilesList
+        }
+    }
+
+    private data class RemoveTiles(val tileSpecs: Collection<TileSpec>) : ChangeAction {
+        override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+            return currentTiles.toMutableList().apply { removeAll(tileSpecs) }
+        }
+    }
+
+    private data class ChangeTiles(
+        val newTiles: List<TileSpec>,
+    ) : ChangeAction {
+        override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+            val new = newTiles.filter { it !is TileSpec.Invalid }
+            return if (new.isNotEmpty()) new else currentTiles
+        }
+    }
+
+    private data class RestoreTiles(
+        val restoreData: RestoreData,
+        val currentAutoAdded: Set<TileSpec>,
+    ) : ChangeAction {
+
+        override fun apply(currentTiles: List<TileSpec>): List<TileSpec> {
+            return reconcileTiles(currentTiles, currentAutoAdded, restoreData)
+        }
+    }
+
+    companion object {
+        private const val SETTING = Settings.Secure.QS_TILES
+        private const val DELIMITER = TilesSettingConverter.DELIMITER
+        // We want a small buffer in case multiple changes come in at the same time (sometimes
+        // happens in first start. This should be enough to not lose changes.
+        private const val CHANGES_BUFFER_SIZE = 10
+
+        private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+        fun reconcileTiles(
+            currentTiles: List<TileSpec>,
+            currentAutoAdded: Set<TileSpec>,
+            restoreData: RestoreData
+        ): List<TileSpec> {
+            val toRestore = restoreData.restoredTiles.toMutableList()
+            val freshlyAutoAdded =
+                currentAutoAdded.filterNot { it in restoreData.restoredAutoAddedTiles }
+            freshlyAutoAdded
+                .filter { it in currentTiles && it !in restoreData.restoredTiles }
+                .map { it to currentTiles.indexOf(it) }
+                .sortedBy { it.second }
+                .forEachIndexed { iteration, (tile, position) ->
+                    val insertAt = position + iteration
+                    if (insertAt > toRestore.size) {
+                        toRestore.add(tile)
+                    } else {
+                        toRestore.add(insertAt, tile)
+                    }
+                }
+
+            return toRestore
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            userId: Int,
+        ): UserTileSpecRepository
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 5a5e47a..00c2358 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.os.UserHandle
+import android.util.Log
 import com.android.systemui.Dumpable
 import com.android.systemui.ProtoDumpable
 import com.android.systemui.dagger.SysUISingleton
@@ -268,6 +269,7 @@
                             // repository
                             launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) }
                         }
+                        Log.d("Fabian", "Finished resolving tiles")
                     }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
new file mode 100644
index 0000000..9844903
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractor.kt
@@ -0,0 +1,53 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.pipeline.data.repository.AutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.QSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
+
+/**
+ * Interactor in charge of triggering reconciliation after QS Secure Settings are restored. For a
+ * given user, it will trigger the reconciliations in the correct order to prevent race conditions.
+ *
+ * Currently, the order is:
+ * 1. TileSpecRepository, with the restored data and the current (before restore) auto add tiles
+ * 2. AutoAddRepository
+ *
+ * [start] needs to be called to trigger the collection of [QSSettingsRestoredRepository].
+ */
+@SysUISingleton
+class RestoreReconciliationInteractor
+@Inject
+constructor(
+    private val tileSpecRepository: TileSpecRepository,
+    private val autoAddRepository: AutoAddRepository,
+    private val qsSettingsRestoredRepository: QSSettingsRestoredRepository,
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    fun start() {
+        applicationScope.launch(backgroundDispatcher) {
+            qsSettingsRestoredRepository.restoreData.flatMapConcat { data ->
+                autoAddRepository.autoAddedTiles(data.userId)
+                        .take(1)
+                        .map { tiles -> data to tiles }
+            }.collect { (restoreData, autoAdded) ->
+                tileSpecRepository.reconcileRestore(restoreData, autoAdded)
+                autoAddRepository.reconcileRestore(restoreData)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index 0743ba0..1539f05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractor
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
 import javax.inject.Inject
 
@@ -30,11 +31,13 @@
     private val currentTilesInteractor: CurrentTilesInteractor,
     private val autoAddInteractor: AutoAddInteractor,
     private val featureFlags: QSPipelineFlagsRepository,
+    private val restoreReconciliationInteractor: RestoreReconciliationInteractor,
 ) : CoreStartable {
 
     override fun start() {
         if (featureFlags.pipelineAutoAddEnabled) {
             autoAddInteractor.init(currentTilesInteractor)
+            restoreReconciliationInteractor.start()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index 573cb715..bca86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -16,11 +16,13 @@
 
 package com.android.systemui.qs.pipeline.shared.logging
 
-import android.annotation.UserIdInt
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
+import com.android.systemui.qs.pipeline.dagger.QSRestoreLog
 import com.android.systemui.qs.pipeline.dagger.QSTileListLog
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.UserTileSpecRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
 
@@ -34,11 +36,13 @@
 constructor(
     @QSTileListLog private val tileListLogBuffer: LogBuffer,
     @QSAutoAddLog private val tileAutoAddLogBuffer: LogBuffer,
+    @QSRestoreLog private val restoreLogBuffer: LogBuffer,
 ) {
 
     companion object {
         const val TILE_LIST_TAG = "QSTileListLog"
         const val AUTO_ADD_TAG = "QSAutoAddableLog"
+        const val RESTORE_TAG = "QSRestoreLog"
     }
 
     /**
@@ -60,20 +64,37 @@
         )
     }
 
-    /**
-     * Logs when the tiles change in Settings.
-     *
-     * This could be caused by SystemUI, or restore.
-     */
-    fun logTilesChangedInSettings(newTiles: String, @UserIdInt user: Int) {
+    fun logTilesRestoredAndReconciled(
+        currentTiles: List<TileSpec>,
+        reconciledTiles: List<TileSpec>,
+        user: Int,
+    ) {
         tileListLogBuffer.log(
             TILE_LIST_TAG,
-            LogLevel.VERBOSE,
+            LogLevel.DEBUG,
             {
-                str1 = newTiles
+                str1 = currentTiles.toString()
+                str2 = reconciledTiles.toString()
                 int1 = user
             },
-            { "Tiles changed in settings for user $int1: $str1" }
+            { "Tiles restored and reconciled for user: $int1\nWas: $str1\nSet to: $str2" }
+        )
+    }
+
+    fun logProcessTileChange(
+        action: UserTileSpecRepository.ChangeAction,
+        newList: List<TileSpec>,
+        userId: Int,
+    ) {
+        tileListLogBuffer.log(
+            TILE_LIST_TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = action.toString()
+                str2 = newList.toString()
+                int1 = userId
+            },
+            { "Processing $str1 for user $int1\nNew list: $str2" }
         )
     }
 
@@ -139,6 +160,30 @@
         )
     }
 
+    fun logAutoAddTilesParsed(userId: Int, tiles: Set<TileSpec>) {
+        tileAutoAddLogBuffer.log(
+            AUTO_ADD_TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = tiles.toString()
+                int1 = userId
+            },
+            { "Auto add tiles parsed for user $int1: $str1" }
+        )
+    }
+
+    fun logAutoAddTilesRestoredReconciled(userId: Int, tiles: Set<TileSpec>) {
+        tileAutoAddLogBuffer.log(
+            AUTO_ADD_TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = tiles.toString()
+                int1 = userId
+            },
+            { "Auto-add tiles reconciled for user $int1: $str1" }
+        )
+    }
+
     fun logTileAutoAdded(userId: Int, spec: TileSpec, position: Int) {
         tileAutoAddLogBuffer.log(
             AUTO_ADD_TAG,
@@ -164,6 +209,23 @@
         )
     }
 
+    fun logSettingsRestored(restoreData: RestoreData) {
+        restoreLogBuffer.log(
+            RESTORE_TAG,
+            LogLevel.DEBUG,
+            {
+                int1 = restoreData.userId
+                str1 = restoreData.restoredTiles.toString()
+                str2 = restoreData.restoredAutoAddedTiles.toString()
+            },
+            {
+                "Restored settings data for user $int1\n" +
+                    "\tRestored tiles: $str1\n" +
+                    "\tRestored auto added tiles: $str2"
+            }
+        )
+    }
+
     /** Reasons for destroying an existing tile. */
     enum class TileDestroyedReason(val readable: String) {
         TILE_REMOVED("Tile removed from  current set"),
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 8b437c3..64006fe 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
 
 import android.content.Context
 import android.os.Bundle
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
index 2f10ad3..47e28d8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/MediaProjectionPermissionDialog.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
 
 import android.content.Context
 import android.media.projection.MediaProjectionConfig
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 3501b6b..7cdb655 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -41,10 +41,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.res.R;
 import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
-import com.android.systemui.res.R;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
 import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 3aab3bf..b80a01212 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -51,7 +51,7 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
 
 import java.io.Closeable;
 import java.io.File;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index f2e94e9..f0ce8a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -18,7 +18,7 @@
 
 import static android.app.Activity.RESULT_OK;
 
-import static com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
+import static com.android.systemui.media.MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET;
 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.INTERNAL;
 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC;
 import static com.android.systemui.screenrecord.ScreenRecordingAudioSource.MIC_AND_INTERNAL;
@@ -41,8 +41,8 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
 import com.android.systemui.res.R;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
index a1d5d98..b5b7043 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
@@ -32,14 +32,10 @@
 import android.widget.Spinner
 import android.widget.Switch
 import androidx.annotation.LayoutRes
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
-import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
-import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
-import com.android.systemui.mediaprojection.permission.SINGLE_APP
-import com.android.systemui.mediaprojection.permission.ScreenShareOption
-import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.media.MediaProjectionAppSelectorActivity
+import com.android.systemui.media.MediaProjectionCaptureTarget
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserContextProvider
 
 /** Dialog to select screen recording options */
@@ -62,7 +58,6 @@
     private lateinit var tapsView: View
     private lateinit var audioSwitch: Switch
     private lateinit var options: Spinner
-
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setDialogTitle(R.string.screenrecord_permission_dialog_title)
@@ -182,7 +177,6 @@
             )
         private const val DELAY_MS: Long = 3000
         private const val INTERVAL_MS: Long = 1000
-
         private fun createOptionList(): List<ScreenShareOption> {
             return listOf(
                 ScreenShareOption(
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
index 37e8d9f..ebf0dd2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/ScreenShareOption.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenShareOption.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.mediaprojection.permission
+package com.android.systemui.screenrecord
 
 import androidx.annotation.IntDef
 import androidx.annotation.StringRes
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index f08eb14..4b3bd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -53,9 +53,6 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
-import android.media.AudioAttributes;
-import android.media.AudioSystem;
-import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Process;
@@ -86,8 +83,6 @@
 import android.window.OnBackInvokedDispatcher;
 import android.window.WindowContext;
 
-import androidx.concurrent.futures.CallbackToFutureAdapter;
-
 import com.android.internal.app.ChooserActivity;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.PhoneWindow;
@@ -108,7 +103,6 @@
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 
-import java.io.File;
 import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
@@ -116,11 +110,11 @@
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
+import javax.inject.Provider;
+
 
 /**
  * Controls the state and flow for screenshots.
@@ -274,7 +268,8 @@
     private final WindowManager mWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
     private final AccessibilityManager mAccessibilityManager;
-    private final ListenableFuture<MediaPlayer> mCameraSound;
+    @Nullable
+    private final ScreenshotSoundController mScreenshotSoundController;
     private final ScrollCaptureClient mScrollCaptureClient;
     private final PhoneWindow mWindow;
     private final DisplayManager mDisplayManager;
@@ -339,6 +334,7 @@
             UserManager userManager,
             AssistContentRequester assistContentRequester,
             MessageContainerController messageContainerController,
+            Provider<ScreenshotSoundController> screenshotSoundController,
             @Assisted int displayId
     ) {
         mScreenshotSmartActions = screenshotSmartActions;
@@ -387,8 +383,12 @@
         mConfigChanges.applyNewConfig(context.getResources());
         reloadAssets();
 
-        // Setup the Camera shutter sound
-        mCameraSound = loadCameraSound();
+        // Sound is only reproduced from the controller of the default display.
+        if (displayId == Display.DEFAULT_DISPLAY) {
+            mScreenshotSoundController = screenshotSoundController.get();
+        } else {
+            mScreenshotSoundController = null;
+        }
 
         mCopyBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -573,17 +573,8 @@
     }
 
     private void releaseMediaPlayer() {
-        // Note that this may block if the sound is still being loaded (very unlikely) but we can't
-        // reliably release in the background because the service is being destroyed.
-        try {
-            MediaPlayer player = mCameraSound.get(1, TimeUnit.SECONDS);
-            if (player != null) {
-                player.release();
-            }
-        } catch (InterruptedException | ExecutionException | TimeoutException e) {
-            mCameraSound.cancel(true);
-            Log.w(TAG, "Error releasing shutter sound", e);
-        }
+        if (mScreenshotSoundController == null) return;
+        mScreenshotSoundController.releaseScreenshotSound();
     }
 
     private void respondToKeyDismissal() {
@@ -889,39 +880,10 @@
         }
     }
 
-    private ListenableFuture<MediaPlayer> loadCameraSound() {
-        // The media player creation is slow and needs on the background thread.
-        return CallbackToFutureAdapter.getFuture((completer) -> {
-            mBgExecutor.execute(() -> {
-                try {
-                    MediaPlayer player = MediaPlayer.create(mContext,
-                            Uri.fromFile(new File(mContext.getResources().getString(
-                                    com.android.internal.R.string.config_cameraShutterSound))),
-                            null,
-                            new AudioAttributes.Builder()
-                                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-                                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                                    .build(), AudioSystem.newAudioSessionId());
-                    completer.set(player);
-                } catch (IllegalStateException e) {
-                    Log.w(TAG, "Screenshot sound initialization failed", e);
-                    completer.set(null);
-                }
-            });
-            return "ScreenshotController#loadCameraSound";
-        });
-    }
-
-    private void playCameraSound() {
-        mCameraSound.addListener(() -> {
-            try {
-                MediaPlayer player = mCameraSound.get();
-                if (player != null) {
-                    player.start();
-                }
-            } catch (InterruptedException | ExecutionException e) {
-            }
-        }, mBgExecutor);
+    private void playCameraSoundIfNeeded() {
+        if (mScreenshotSoundController == null) return;
+        // the controller is not-null only on the default display controller
+        mScreenshotSoundController.playCameraSound();
     }
 
     /**
@@ -930,7 +892,7 @@
      */
     private void saveScreenshotAndToast(UserHandle owner, Consumer<Uri> finisher) {
         // Play the shutter sound to notify that we've taken a screenshot
-        playCameraSound();
+        playCameraSoundIfNeeded();
 
         saveScreenshotInWorkerThread(
                 owner,
@@ -974,7 +936,7 @@
         }
 
         // Play the shutter sound to notify that we've taken a screenshot
-        playCameraSound();
+        playCameraSoundIfNeeded();
 
         if (DEBUG_ANIM) {
             Log.d(TAG, "starting post-screenshot animation");
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
new file mode 100644
index 0000000..cd0cab5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.screenshot
+
+import android.media.MediaPlayer
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.TraceUtils.Companion.tracedAsync
+import com.google.errorprone.annotations.CanIgnoreReturnValue
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.withTimeout
+
+/** Controls sound reproduction after a screenshot is taken. */
+interface ScreenshotSoundController {
+    /** Reproduces the camera sound. */
+    @CanIgnoreReturnValue fun playCameraSound(): Deferred<Unit>
+
+    /** Releases the sound. [playCameraSound] behaviour is undefined after this has been called. */
+    @CanIgnoreReturnValue fun releaseScreenshotSound(): Deferred<Unit>
+}
+
+class ScreenshotSoundControllerImpl
+@Inject
+constructor(
+    private val soundProvider: ScreenshotSoundProvider,
+    @Application private val coroutineScope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher
+) : ScreenshotSoundController {
+
+    val player: Deferred<MediaPlayer?> =
+        coroutineScope.tracedAsync("loadCameraSound", bgDispatcher) {
+            try {
+                soundProvider.getScreenshotSound()
+            } catch (e: IllegalStateException) {
+                Log.w(TAG, "Screenshot sound initialization failed", e)
+                null
+            }
+        }
+
+    override fun playCameraSound(): Deferred<Unit> {
+        return coroutineScope.tracedAsync("playCameraSound", bgDispatcher) {
+            player.await()?.start()
+        }
+    }
+    override fun releaseScreenshotSound(): Deferred<Unit> {
+        return coroutineScope.tracedAsync("releaseScreenshotSound", bgDispatcher) {
+            try {
+                withTimeout(1.seconds) { player.await()?.release() }
+            } catch (e: TimeoutCancellationException) {
+                player.cancel()
+                Log.w(TAG, "Error releasing shutter sound", e)
+            }
+        }
+    }
+
+    private companion object {
+        const val TAG = "ScreenshotSoundControllerImpl"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
new file mode 100644
index 0000000..73151d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundProvider.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.screenshot
+
+import android.content.Context
+import android.media.AudioAttributes
+import android.media.AudioSystem
+import android.media.MediaPlayer
+import android.net.Uri
+import com.android.internal.R
+import com.android.systemui.dagger.SysUISingleton
+import java.io.File
+import javax.inject.Inject
+
+/** Provides a [MediaPlayer] that reproduces the screenshot sound. */
+interface ScreenshotSoundProvider {
+
+    /**
+     * Creates a new [MediaPlayer] that reproduces the screenshot sound. This should be called from
+     * a background thread, as it might take time.
+     */
+    fun getScreenshotSound(): MediaPlayer
+}
+
+@SysUISingleton
+class ScreenshotSoundProviderImpl
+@Inject
+constructor(
+    private val context: Context,
+) : ScreenshotSoundProvider {
+    override fun getScreenshotSound(): MediaPlayer {
+        return MediaPlayer.create(
+            context,
+            Uri.fromFile(File(context.resources.getString(R.string.config_cameraShutterSound))),
+            /* holder = */ null,
+            AudioAttributes.Builder()
+                .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                .build(),
+            AudioSystem.newAudioSessionId()
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 7d17d4c..3797b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -25,6 +25,10 @@
 import com.android.systemui.screenshot.ScreenshotPolicyImpl;
 import com.android.systemui.screenshot.ScreenshotProxyService;
 import com.android.systemui.screenshot.ScreenshotRequestProcessor;
+import com.android.systemui.screenshot.ScreenshotSoundController;
+import com.android.systemui.screenshot.ScreenshotSoundControllerImpl;
+import com.android.systemui.screenshot.ScreenshotSoundProvider;
+import com.android.systemui.screenshot.ScreenshotSoundProviderImpl;
 import com.android.systemui.screenshot.TakeScreenshotService;
 import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
 import com.android.systemui.screenshot.appclips.AppClipsService;
@@ -69,4 +73,12 @@
     @Binds
     abstract ScreenshotRequestProcessor bindScreenshotRequestProcessor(
             RequestProcessor requestProcessor);
+
+    @Binds
+    abstract ScreenshotSoundProvider bindScreenshotSoundProvider(
+            ScreenshotSoundProviderImpl screenshotSoundProviderImpl);
+
+    @Binds
+    abstract ScreenshotSoundController bindScreenshotSoundController(
+            ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index e8be40e..9b74ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -791,7 +791,8 @@
     /** update Qs height state */
     public void setExpansionHeight(float height) {
         // TODO(b/277909752): remove below log when bug is fixed
-        if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0) {
+        if (mSplitShadeEnabled && mShadeExpandedFraction == 1.0f && height == 0
+                && mBarState == SHADE) {
             Log.wtf(TAG,
                     "setting QS height to 0 in split shade while shade is open(ing). "
                             + "Value of mExpandImmediate = " + mExpandImmediate);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 670fb12..93bb435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -172,6 +172,7 @@
     private static final int MSG_LOCK_TASK_MODE_CHANGED = 75 << MSG_SHIFT;
     private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
     private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
+    private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
     public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -301,6 +302,8 @@
 
         default void addQsTile(ComponentName tile) { }
         default void remQsTile(ComponentName tile) { }
+
+        default void setQsTiles(String[] tiles) {}
         default void clickTile(ComponentName tile) { }
 
         default void handleSystemKey(KeyEvent arg1) { }
@@ -903,6 +906,13 @@
     }
 
     @Override
+    public void setQsTiles(String[] tiles) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SET_QS_TILES, tiles).sendToTarget();
+        }
+    }
+
+    @Override
     public void clickQsTile(ComponentName tile) {
         synchronized (mLock) {
             mHandler.obtainMessage(MSG_CLICK_QS_TILE, tile).sendToTarget();
@@ -1533,6 +1543,11 @@
                         mCallbacks.get(i).remQsTile((ComponentName) msg.obj);
                     }
                     break;
+                case MSG_SET_QS_TILES:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).setQsTiles((String[]) msg.obj);
+                    }
+                    break;
                 case MSG_CLICK_QS_TILE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).clickTile((ComponentName) msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index e632214..37a4ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -104,8 +104,9 @@
     // Record the HISTORY_SIZE most recent states
     private int mHistoryIndex = 0;
     private HistoricalState[] mHistoricalRecords = new HistoricalState[HISTORY_SIZE];
-    // This is used by InteractionJankMonitor to get callback from HWUI.
+    // These views are used by InteractionJankMonitor to get callback from HWUI.
     private View mView;
+    private KeyguardClockSwitch mClockSwitchView;
 
     /**
      * If any of the system bars is hidden.
@@ -334,6 +335,7 @@
         if ((mView == null || !mView.isAttachedToWindow())
                 && (view != null && view.isAttachedToWindow())) {
             mView = view;
+            mClockSwitchView = view.findViewById(R.id.keyguard_clock_container);
         }
         mDozeAmountTarget = dozeAmount;
         if (animated) {
@@ -416,21 +418,12 @@
 
     /** Returns the id of the currently rendering clock */
     public String getClockId() {
-        if (mView == null) {
-            return KeyguardClockSwitch.MISSING_CLOCK_ID;
-        }
-
-        View clockSwitch = mView.findViewById(R.id.keyguard_clock_container);
-        if (clockSwitch == null) {
+        if (mClockSwitchView == null) {
             Log.e(TAG, "Clock container was missing");
             return KeyguardClockSwitch.MISSING_CLOCK_ID;
         }
-        if (!(clockSwitch instanceof KeyguardClockSwitch)) {
-            Log.e(TAG, "Clock container was incorrect type: " + clockSwitch);
-            return KeyguardClockSwitch.MISSING_CLOCK_ID;
-        }
 
-        return ((KeyguardClockSwitch) clockSwitch).getClockId();
+        return mClockSwitchView.getClockId();
     }
 
     private void beginInteractionJankMonitor() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 88994b9..6ec9dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -100,7 +100,11 @@
         /**
          * The notification is coming from a suspended packages, so FSI is suppressed.
          */
-        NO_FSI_SUSPENDED(false);
+        NO_FSI_SUSPENDED(false),
+        /**
+         * The device is not provisioned, launch FSI.
+         */
+        FSI_NOT_PROVISIONED(true);
 
         public final boolean shouldLaunch;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 0c43da0..3819843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -75,6 +76,7 @@
     private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
     private final UiEventLogger mUiEventLogger;
     private final UserTracker mUserTracker;
+    private final DeviceProvisionedController mDeviceProvisionedController;
 
     @VisibleForTesting
     protected boolean mUseHeadsUp = false;
@@ -121,7 +123,8 @@
             NotifPipelineFlags flags,
             KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
             UiEventLogger uiEventLogger,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            DeviceProvisionedController deviceProvisionedController) {
         mContentResolver = contentResolver;
         mPowerManager = powerManager;
         mBatteryController = batteryController;
@@ -163,6 +166,7 @@
                     headsUpObserver);
         }
         headsUpObserver.onChange(true); // set up
+        mDeviceProvisionedController = deviceProvisionedController;
     }
 
     @Override
@@ -334,6 +338,12 @@
             }
         }
 
+        // The device is not provisioned, launch FSI.
+        if (!mDeviceProvisionedController.isDeviceProvisioned()) {
+            return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_NOT_PROVISIONED,
+                    suppressedByDND);
+        }
+
         // Detect the case determined by b/231322873 to launch FSI while device is in use,
         // as blocked by the correct implementation, and report the event.
         return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 4114eb2..a8d59d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -71,15 +71,15 @@
     private var appUid: Int? = null
     private var packageName: String? = null
     private var appName: String? = null
+    private var channel: NotificationChannel? = null
     private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
 
     // Caller should set this if they care about when we dismiss
     var onFinishListener: OnChannelEditorDialogFinishedListener? = null
 
-    @VisibleForTesting
-    internal val paddedChannels = mutableListOf<NotificationChannel>()
     // Channels handed to us from NotificationInfo
-    private val providedChannels = mutableListOf<NotificationChannel>()
+    @VisibleForTesting
+    internal val channelList = mutableListOf<NotificationChannel>()
 
     // Map from NotificationChannel to importance
     private val edits = mutableMapOf<NotificationChannel, Int>()
@@ -93,14 +93,14 @@
     private val channelGroupList = mutableListOf<NotificationChannelGroup>()
 
     /**
-     * Give the controller all of the information it needs to present the dialog
+     * Give the controller all the information it needs to present the dialog
      * for a given app. Does a bunch of querying of NoMan, but won't present anything yet
      */
     fun prepareDialogForApp(
         appName: String,
         packageName: String,
         uid: Int,
-        channels: Set<NotificationChannel>,
+        channel: NotificationChannel,
         appIcon: Drawable,
         onSettingsClickListener: NotificationInfo.OnSettingsClickListener?
     ) {
@@ -110,6 +110,7 @@
         this.appIcon = appIcon
         this.appNotificationsEnabled = checkAreAppNotificationsOn()
         this.onSettingsClickListener = onSettingsClickListener
+        this.channel = channel
 
         // These will always start out the same
         appNotificationsCurrentlyEnabled = appNotificationsEnabled
@@ -117,9 +118,7 @@
         channelGroupList.clear()
         channelGroupList.addAll(fetchNotificationChannelGroups())
         buildGroupNameLookup()
-        providedChannels.clear()
-        providedChannels.addAll(channels)
-        padToFourChannels(channels)
+        populateChannelList()
         initDialog()
 
         prepared = true
@@ -133,36 +132,26 @@
         }
     }
 
-    private fun padToFourChannels(channels: Set<NotificationChannel>) {
-        paddedChannels.clear()
-        // First, add all of the given channels
-        paddedChannels.addAll(channels.asSequence().take(4))
-
-        // Then pad to 4 if we haven't been given that many
-        paddedChannels.addAll(getDisplayableChannels(channelGroupList.asSequence())
-                .filterNot { paddedChannels.contains(it) }
-                .distinct()
-                .take(4 - paddedChannels.size))
-
-        // If we only got one channel and it has the default miscellaneous tag, then we actually
-        // are looking at an app with a targetSdk <= O, and it doesn't make much sense to show the
-        // channel
-        if (paddedChannels.size == 1 && DEFAULT_CHANNEL_ID == paddedChannels[0].id) {
-            paddedChannels.clear()
+    private fun populateChannelList() {
+        channelList.clear()
+        if (DEFAULT_CHANNEL_ID != channel!!.id) {
+            channelList.add(0, channel!!)
+            channelList.addAll(getDisplayableChannels(channelGroupList.asSequence())
+                    .filterNot { it.id == channel!!.id }
+                    .distinct())
         }
     }
 
     private fun getDisplayableChannels(
         groupList: Sequence<NotificationChannelGroup>
     ): Sequence<NotificationChannel> {
-
-        // TODO (b/194833441): remove channel level settings when we move to a permission
         val channels = groupList
                 .flatMap { group ->
-                    group.channels.asSequence().filterNot { channel ->
-                        channel.importance == IMPORTANCE_NONE ||
+                    group.channels.asSequence()
+                            .sortedWith(compareBy {group.name?.toString() ?: group.id})
+                            .filterNot { channel ->
                                 channel.isImportanceLockedByCriticalDeviceFunction
-                    }
+                            }
                 }
 
         // TODO: sort these by avgSentWeekly, but for now let's just do alphabetical (why not)
@@ -196,8 +185,7 @@
         appNotificationsCurrentlyEnabled = null
 
         edits.clear()
-        paddedChannels.clear()
-        providedChannels.clear()
+        channelList.clear()
         groupNameLookup.clear()
     }
 
@@ -231,7 +219,7 @@
     @Suppress("unchecked_cast")
     private fun fetchNotificationChannelGroups(): List<NotificationChannelGroup> {
         return try {
-            noMan.getNotificationChannelGroupsForPackage(packageName!!, appUid!!, false)
+            noMan.getRecentBlockedNotificationChannelGroupsForPackage(packageName!!, appUid!!)
                     .list as? List<NotificationChannelGroup> ?: listOf()
         } catch (e: Exception) {
             Log.e(TAG, "Error fetching channel groups", e)
@@ -280,7 +268,6 @@
 
     @VisibleForTesting
     fun launchSettings(sender: View) {
-        val channel = if (providedChannels.size == 1) providedChannels[0] else null
         onSettingsClickListener?.onClick(sender, channel, appUid!!)
     }
 
@@ -301,14 +288,12 @@
                 controller = this@ChannelEditorDialogController
                 appIcon = this@ChannelEditorDialogController.appIcon
                 appName = this@ChannelEditorDialogController.appName
-                channels = paddedChannels
+                channels = channelList
             }
 
             setOnShowListener {
-                // play a highlight animation for the given channels
-                for (channel in providedChannels) {
-                    listView?.highlightChannel(channel)
-                }
+                // play a highlight animation for the given channel
+                listView?.highlightChannel(channel!!)
             }
 
             findViewById<TextView>(R.id.done_button)?.setOnClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 2cfd075..10e67a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator
 import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
+import android.app.NotificationManager.IMPORTANCE_LOW
 import android.app.NotificationManager.IMPORTANCE_NONE
 import android.app.NotificationManager.IMPORTANCE_UNSPECIFIED
 import android.content.Context
@@ -55,12 +56,14 @@
 
     // The first row is for the entire app
     private lateinit var appControlRow: AppControlView
+    private lateinit var channelListView: LinearLayout
     private val channelRows = mutableListOf<ChannelRow>()
 
     override fun onFinishInflate() {
         super.onFinishInflate()
 
         appControlRow = requireViewById(R.id.app_control)
+        channelListView = requireViewById(R.id.scrollView)
     }
 
     /**
@@ -102,7 +105,7 @@
 
         // Remove any rows
         for (row in channelRows) {
-            removeView(row)
+            channelListView.removeView(row)
         }
         channelRows.clear()
 
@@ -122,7 +125,7 @@
         row.channel = channel
 
         channelRows.add(row)
-        addView(row)
+        channelListView.addView(row)
     }
 
     private fun updateAppControlRow(enabled: Boolean) {
@@ -179,7 +182,9 @@
         switch = requireViewById(R.id.toggle)
         switch.setOnCheckedChangeListener { _, b ->
             channel?.let {
-                controller.proposeEditForChannel(it, if (b) it.importance else IMPORTANCE_NONE)
+                controller.proposeEditForChannel(it,
+                        if (b) it.originalImportance.coerceAtLeast(IMPORTANCE_LOW)
+                        else IMPORTANCE_NONE)
             }
         }
         setOnClickListener { switch.toggle() }
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 fb8024c..d18f991 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
@@ -2658,42 +2658,6 @@
     }
 
     /**
-     * Returns the number of channels covered by the notification row (including its children if
-     * it's a summary notification).
-     */
-    public int getNumUniqueChannels() {
-        return getUniqueChannels().size();
-    }
-
-    /**
-     * Returns the channels covered by the notification row (including its children if
-     * it's a summary notification).
-     */
-    public ArraySet<NotificationChannel> getUniqueChannels() {
-        ArraySet<NotificationChannel> channels = new ArraySet<>();
-
-        channels.add(mEntry.getChannel());
-
-        // If this is a summary, then add in the children notification channels for the
-        // same user and pkg.
-        if (mIsSummaryWithChildren) {
-            final List<ExpandableNotificationRow> childrenRows = getAttachedChildren();
-            final int numChildren = childrenRows.size();
-            for (int i = 0; i < numChildren; i++) {
-                final ExpandableNotificationRow childRow = childrenRows.get(i);
-                final NotificationChannel childChannel = childRow.getEntry().getChannel();
-                final StatusBarNotification childSbn = childRow.getEntry().getSbn();
-                if (childSbn.getUser().equals(mEntry.getSbn().getUser())
-                        && childSbn.getPackageName().equals(mEntry.getSbn().getPackageName())) {
-                    channels.add(childChannel);
-                }
-            }
-        }
-
-        return channels;
-    }
-
-    /**
      * If this is a group, update the appearance of the children.
      */
     public void updateChildrenAppearance() {
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 1dd3739..6d65660 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
@@ -403,7 +403,6 @@
                 mChannelEditorDialogController,
                 packageName,
                 row.getEntry().getChannel(),
-                row.getUniqueChannels(),
                 row.getEntry(),
                 onSettingsClick,
                 onAppSettingsClick,
@@ -449,7 +448,6 @@
                 mChannelEditorDialogController,
                 packageName,
                 row.getEntry().getChannel(),
-                row.getUniqueChannels(),
                 row.getEntry(),
                 onSettingsClick,
                 mDeviceProvisionedController.isDeviceProvisioned(),
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 d8f31d4..d8ebd42 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,8 +104,6 @@
     private String mAppName;
     private int mAppUid;
     private String mDelegatePkg;
-    private int mNumUniqueChannelsInRow;
-    private Set<NotificationChannel> mUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
     private int mStartingChannelImportance;
     private boolean mWasShownHighPriority;
@@ -196,7 +194,6 @@
             ChannelEditorDialogController channelEditorDialogController,
             String pkg,
             NotificationChannel notificationChannel,
-            Set<NotificationChannel> uniqueChannelsInRow,
             NotificationEntry entry,
             OnSettingsClickListener onSettingsClick,
             OnAppSettingsClickListener onAppSettingsClick,
@@ -213,8 +210,6 @@
         mChannelEditorDialogController = channelEditorDialogController;
         mAssistantFeedbackController = assistantFeedbackController;
         mPackageName = pkg;
-        mUniqueChannelsInRow = uniqueChannelsInRow;
-        mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
         mEntry = entry;
         mSbn = entry.getSbn();
         mPm = pm;
@@ -236,15 +231,8 @@
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
-        if (mNumUniqueChannelsInRow == 0) {
-            throw new IllegalArgumentException("bindNotification requires at least one channel");
-        } else  {
-            // Special behavior for the Default channel if no other channels have been defined.
-            mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
-                    && mSingleNotificationChannel.getId().equals(
-                            NotificationChannel.DEFAULT_CHANNEL_ID)
-                    && numTotalChannels == 1;
-        }
+        mIsSingleDefaultChannel = mSingleNotificationChannel.getId().equals(
+                NotificationChannel.DEFAULT_CHANNEL_ID) && numTotalChannels == 1;
         mIsAutomaticChosen = getAlertingBehavior() == BEHAVIOR_AUTOMATIC;
 
         bindHeader();
@@ -271,11 +259,6 @@
             findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
             ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
             findViewById(R.id.turn_off_notifications).setVisibility(GONE);
-        } else if (mNumUniqueChannelsInRow > 1) {
-            findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
-            findViewById(R.id.non_configurable_text).setVisibility(GONE);
-            findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
-            findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
         } else {
             findViewById(R.id.non_configurable_call_text).setVisibility(GONE);
             findViewById(R.id.non_configurable_text).setVisibility(GONE);
@@ -361,9 +344,7 @@
         if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
             final int appUidF = mAppUid;
             return ((View view) -> {
-                mOnSettingsClickListener.onClick(view,
-                        mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
-                        appUidF);
+                mOnSettingsClickListener.onClick(view, mSingleNotificationChannel, appUidF);
             });
         }
         return null;
@@ -375,7 +356,7 @@
                 mPresentingChannelEditorDialog = true;
 
                 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
-                        mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+                        mSingleNotificationChannel, mPkgIcon, mOnSettingsClickListener);
                 mChannelEditorDialogController.setOnFinishListener(() -> {
                     mPresentingChannelEditorDialog = false;
                     mGutsContainer.closeControls(this, false);
@@ -392,7 +373,7 @@
 
     private void bindName() {
         final TextView channelName = findViewById(R.id.channel_name);
-        if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
+        if (mIsSingleDefaultChannel) {
             channelName.setVisibility(View.GONE);
         } else {
             channelName.setText(mSingleNotificationChannel.getName());
@@ -459,7 +440,7 @@
             Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
             bgHandler.post(
                     new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
-                            mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+                            mSingleNotificationChannel,
                             mStartingChannelImportance, newImportance, mIsAutomaticChosen));
             mOnUserInteractionCallback.onImportanceChanged(mEntry);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 06c3b79..53f7d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -57,7 +57,6 @@
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
     private boolean mIsNonBlockable;
-    private Set<NotificationChannel> mUniqueChannelsInRow;
     private Drawable mPkgIcon;
 
     private boolean mPresentingChannelEditorDialog = false;
@@ -83,7 +82,6 @@
             ChannelEditorDialogController channelEditorDialogController,
             String pkg,
             NotificationChannel notificationChannel,
-            Set<NotificationChannel> uniqueChannelsInRow,
             NotificationEntry entry,
             NotificationInfo.OnSettingsClickListener onSettingsClick,
             boolean isDeviceProvisioned,
@@ -100,7 +98,6 @@
         mIsDeviceProvisioned = isDeviceProvisioned;
         mIsNonBlockable = isNonBlockable;
         mChannelEditorDialogController = channelEditorDialogController;
-        mUniqueChannelsInRow = uniqueChannelsInRow;
 
         bindHeader();
         bindActions();
@@ -149,7 +146,7 @@
                 mPresentingChannelEditorDialog = true;
 
                 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
-                        mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+                        mNotificationChannel, mPkgIcon, mOnSettingsClickListener);
                 mChannelEditorDialogController.setOnFinishListener(() -> {
                     mPresentingChannelEditorDialog = false;
                     mGutsContainer.closeControls(this, false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 485ab32..f7ff39c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -75,6 +75,7 @@
 
 import dagger.Lazy;
 
+import java.util.Arrays;
 import java.util.Optional;
 
 import javax.inject.Inject;
@@ -201,6 +202,11 @@
     }
 
     @Override
+    public void setQsTiles(String[] tiles) {
+        mQSHost.changeTilesByUser(mQSHost.getSpecs(), Arrays.stream(tiles).toList());
+    }
+
+    @Override
     public void clickTile(ComponentName tile) {
         // Can't inject this because it changes with the QS fragment
         QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 6f69ea81..05beded 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -24,9 +24,11 @@
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+
 import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 import static androidx.lifecycle.Lifecycle.State.RESUMED;
+
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
@@ -121,7 +123,6 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.InitController;
 import com.android.systemui.Prefs;
-import com.android.systemui.res.R;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.assist.AssistManager;
@@ -165,8 +166,9 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.settings.UserTracker;
@@ -244,8 +246,6 @@
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
-import dagger.Lazy;
-
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.List;
@@ -257,6 +257,8 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 
+import dagger.Lazy;
+
 /**
  * A class handling initialization and coordination between some of the key central surfaces in
  * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
@@ -1346,9 +1348,10 @@
                     });
             fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                 QS qs = (QS) f;
-                if (qs instanceof QSFragment) {
-                    mQSPanelController = ((QSFragment) qs).getQSPanelController();
-                    ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
+                if (qs instanceof QSFragmentLegacy) {
+                    QSFragmentLegacy qsFragment = (QSFragmentLegacy) qs;
+                    mQSPanelController = qsFragment.getQSPanelController();
+                    qsFragment.setBrightnessMirrorController(mBrightnessMirrorController);
                 }
             });
         }
@@ -1502,7 +1505,7 @@
     protected QS createDefaultQSFragment() {
         return mFragmentService
                 .getFragmentHostManager(getNotificationShadeWindowView())
-                .create(QSFragment.class);
+                .create(QSFragmentLegacy.class);
     }
 
     private void setUpPresenter() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index b53939e..a27e67b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -112,6 +112,9 @@
         mDisplayCutout = null;
     }
 
+    // Per b/300629388, we let the PhoneStatusBarView detect onConfigurationChanged to
+    // updateResources, instead of letting the PhoneStatusBarViewController detect onConfigChanged
+    // then notify PhoneStatusBarView.
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index e1096e2..f9702dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.statusbar.phone
 
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
-import android.content.res.Configuration
 import android.graphics.Point
 import android.util.Log
 import android.view.MotionEvent
@@ -72,10 +71,6 @@
 
     private val configurationListener =
         object : ConfigurationController.ConfigurationListener {
-            override fun onConfigChanged(newConfig: Configuration?) {
-                mView.updateResources()
-            }
-
             override fun onDensityOrFontScaleChanged() {
                 mView.onDensityOrFontScaleChanged()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 02473f2..aacdc63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -186,6 +186,10 @@
             }
         )
     }
+
+    fun logOnSimStateChanged() {
+        buffer.log(TAG, LogLevel.INFO, "onSimStateChanged")
+    }
 }
 
 private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index ea77163..cf1c97c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -90,4 +90,12 @@
 
     /** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
     val defaultMobileIconGroup: Flow<MobileIconGroup>
+
+    /**
+     * If any active SIM on the device is in
+     * [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
+     * [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
+     * [android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED]
+     */
+    val isAnySimSecure: Flow<Boolean>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 991ff56..2291631 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -151,6 +151,8 @@
     override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
         activeRepo.flatMapLatest { it.defaultMobileIconGroup }
 
+    override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
     override val defaultDataSubId: StateFlow<Int> =
         activeRepo
             .flatMapLatest { it.defaultDataSubId }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index ee13d93..c7987e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -134,6 +134,8 @@
 
     override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
 
+    override val isAnySimSecure: Flow<Boolean> = flowOf(false)
+
     override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index cd68621..dc50990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -71,6 +71,7 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.mapNotNull
@@ -181,6 +182,7 @@
                 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
+            .flowOn(bgDispatcher)
             .scan(initial = initial) { state, event -> state.applyEvent(event) }
             .stateIn(scope = scope, started = SharingStarted.WhileSubscribed(), initial)
     }
@@ -358,6 +360,7 @@
 
                 awaitClose { context.unregisterReceiver(receiver) }
             }
+            .flowOn(bgDispatcher)
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
 
     override val dataEnabled = run {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 74a849a..ecb80f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -28,9 +28,10 @@
 import android.telephony.TelephonyManager
 import androidx.annotation.VisibleForTesting
 import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.SignalIcon.MobileIconGroup
 import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.res.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
@@ -38,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
@@ -94,6 +96,7 @@
     // See [CarrierMergedConnectionRepository] for details.
     wifiRepository: WifiRepository,
     private val fullMobileRepoFactory: FullMobileConnectionRepository.Factory,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) : MobileConnectionsRepository {
     private var subIdRepositoryCache: MutableMap<Int, FullMobileConnectionRepository> =
         mutableMapOf()
@@ -134,22 +137,24 @@
             )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
 
-    private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
-        val callback =
-            object : SubscriptionManager.OnSubscriptionsChangedListener() {
-                override fun onSubscriptionsChanged() {
-                    logger.logOnSubscriptionsChanged()
-                    trySend(Unit)
-                }
+    private val mobileSubscriptionsChangeEvent: Flow<Unit> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : SubscriptionManager.OnSubscriptionsChangedListener() {
+                        override fun onSubscriptionsChanged() {
+                            logger.logOnSubscriptionsChanged()
+                            trySend(Unit)
+                        }
+                    }
+
+                subscriptionManager.addOnSubscriptionsChangedListener(
+                    bgDispatcher.asExecutor(),
+                    callback,
+                )
+
+                awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
             }
-
-        subscriptionManager.addOnSubscriptionsChangedListener(
-            bgDispatcher.asExecutor(),
-            callback,
-        )
-
-        awaitClose { subscriptionManager.removeOnSubscriptionsChangedListener(callback) }
-    }
+            .flowOn(bgDispatcher)
 
     /**
      * State flow that emits the set of mobile data subscriptions, each represented by its own
@@ -184,6 +189,7 @@
                 telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback)
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
+            .flowOn(bgDispatcher)
             .distinctUntilChanged()
             .logDiffsForTable(
                 tableLogger,
@@ -250,6 +256,27 @@
             .distinctUntilChanged()
             .onEach { logger.logDefaultMobileIconGroup(it) }
 
+    override val isAnySimSecure: Flow<Boolean> =
+        conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardUpdateMonitorCallback() {
+                        override fun onSimStateChanged(subId: Int, slotId: Int, simState: Int) {
+                            logger.logOnSimStateChanged()
+                            trySend(keyguardUpdateMonitor.isSimPinSecure)
+                        }
+                    }
+                keyguardUpdateMonitor.registerCallback(callback)
+                trySend(false)
+                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+            }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "isAnySimSecure",
+                initialValue = false,
+            )
+            .distinctUntilChanged()
+
     override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
         getOrCreateRepoForSubId(subId)
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index e489499..ce9d6fd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -37,7 +37,6 @@
 import com.android.internal.util.UserIcons
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.res.R
 import com.android.systemui.SystemUISecondaryUserService
 import com.android.systemui.animation.Expandable
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -50,6 +49,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
 import com.android.systemui.user.CreateUserActivity
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
@@ -62,6 +62,7 @@
 import com.android.systemui.user.utils.MultiUserActionsEvent
 import com.android.systemui.user.utils.MultiUserActionsEventHelper
 import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.UserRestrictionChecker
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -103,6 +104,7 @@
     private val refreshUsersScheduler: RefreshUsersScheduler,
     private val guestUserInteractor: GuestUserInteractor,
     private val uiEventLogger: UiEventLogger,
+    private val userRestrictionChecker: UserRestrictionChecker,
 ) {
     /**
      * Defines interface for classes that can be notified when the state of users on the device is
@@ -593,6 +595,7 @@
                 ) &&
                     // If the user is auto-created is must not be currently resetting.
                     !(isGuestUserAutoCreated && isGuestUserResetting),
+            userRestrictionChecker = userRestrictionChecker,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 93573fa..80139bd 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -22,10 +22,10 @@
 import android.graphics.Bitmap
 import android.os.UserManager
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
-import com.android.settingslib.RestrictedLockUtilsInternal
 import com.android.systemui.res.R
 import com.android.systemui.user.data.source.UserRecord
 import com.android.systemui.user.shared.model.UserActionModel
+import com.android.systemui.utils.UserRestrictionChecker
 
 /**
  * Defines utility functions for helping with legacy data code for users.
@@ -68,6 +68,7 @@
         actionType: UserActionModel,
         isRestricted: Boolean,
         isSwitchToEnabled: Boolean,
+        userRestrictionChecker: UserRestrictionChecker,
     ): UserRecord {
         return UserRecord(
             isGuest = actionType == UserActionModel.ENTER_GUEST_MODE,
@@ -79,6 +80,7 @@
                 getEnforcedAdmin(
                     context = context,
                     selectedUserId = selectedUserId,
+                    userRestrictionChecker = userRestrictionChecker,
                 ),
             isManageUsers = actionType == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
         )
@@ -103,9 +105,10 @@
     private fun getEnforcedAdmin(
         context: Context,
         selectedUserId: Int,
+        userRestrictionChecker: UserRestrictionChecker
     ): EnforcedAdmin? {
         val admin =
-            RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+            userRestrictionChecker.checkIfRestrictionEnforced(
                 context,
                 UserManager.DISALLOW_ADD_USER,
                 selectedUserId,
@@ -113,7 +116,7 @@
                 ?: return null
 
         return if (
-            !RestrictedLockUtilsInternal.hasBaseUserRestriction(
+            !userRestrictionChecker.hasBaseUserRestriction(
                 context,
                 UserManager.DISALLOW_ADD_USER,
                 selectedUserId,
diff --git a/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
new file mode 100644
index 0000000..3f8346b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/utils/UserRestrictionChecker.kt
@@ -0,0 +1,25 @@
+package com.android.systemui.utils
+
+import android.content.Context
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
+import javax.inject.Inject
+
+/** Proxy to call [RestrictedLockUtilsInternal] */
+class UserRestrictionChecker @Inject constructor() {
+    fun checkIfRestrictionEnforced(
+        context: Context,
+        userRestriction: String,
+        userId: Int
+    ): RestrictedLockUtils.EnforcedAdmin? {
+        return RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
+            context,
+            userRestriction,
+            userId
+        )
+    }
+
+    fun hasBaseUserRestriction(context: Context, userRestriction: String, userId: Int): Boolean {
+        return RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
index 8fc63b2..7365460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
@@ -44,6 +44,7 @@
 import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -126,6 +127,7 @@
                 refreshUsersScheduler = refreshUsersScheduler,
                 guestUserInteractor = guestInteractor,
                 uiEventLogger = uiEventLogger,
+                userRestrictionChecker = mock(),
             )
         shadeInteractor =
             ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index c0257df..92c8a39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -395,7 +395,7 @@
             val bouncerSceneKey = currentScene?.key
             assertThat(bouncerSceneKey).isEqualTo(SceneKey.Bouncer)
 
-            underTest.hide("")
+            underTest.onImeHidden()
 
             assertThat(currentScene?.key).isEqualTo(SceneKey.Lockscreen)
         }
@@ -409,7 +409,7 @@
             val notBouncerSceneKey = currentScene?.key
             assertThat(notBouncerSceneKey).isNotEqualTo(SceneKey.Bouncer)
 
-            underTest.hide("")
+            underTest.onImeHidden()
 
             assertThat(currentScene?.key).isEqualTo(notBouncerSceneKey)
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 360fa56..944b059 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -32,7 +32,6 @@
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
-import com.android.systemui.res.R
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -48,6 +47,10 @@
 import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
 import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
 import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.eq
@@ -87,6 +90,7 @@
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var biometricManager: BiometricManager
+    @Mock private lateinit var tableLogger: TableLogBuffer
     @Captor
     private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
     @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -97,6 +101,7 @@
     private lateinit var devicePostureRepository: FakeDevicePostureRepository
     private lateinit var facePropertyRepository: FakeFacePropertyRepository
     private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
 
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var testScope: TestScope
@@ -112,6 +117,8 @@
         devicePostureRepository = FakeDevicePostureRepository()
         facePropertyRepository = FakeFacePropertyRepository()
         fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        mobileConnectionsRepository =
+            FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
     }
 
     private suspend fun createBiometricSettingsRepository() {
@@ -132,6 +139,7 @@
                 dumpManager = dumpManager,
                 facePropertyRepository = facePropertyRepository,
                 fingerprintPropertyRepository = fingerprintPropertyRepository,
+                mobileConnectionsRepository = mobileConnectionsRepository,
             )
         testScope.runCurrent()
         fingerprintPropertyRepository.setProperties(
@@ -421,6 +429,50 @@
         }
 
     @Test
+    fun anySimSecure_disablesFaceAuth() =
+        testScope.runTest {
+            faceAuthIsEnrolled()
+            createBiometricSettingsRepository()
+
+            faceAuthIsEnabledByBiometricManager()
+            doNotDisableKeyguardAuthFeatures()
+            mobileConnectionsRepository.isAnySimSecure.value = false
+            runCurrent()
+
+            val isFaceAuthEnabledAndEnrolled by
+                collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
+
+            assertThat(isFaceAuthEnabledAndEnrolled).isTrue()
+
+            mobileConnectionsRepository.isAnySimSecure.value = true
+            runCurrent()
+
+            assertThat(isFaceAuthEnabledAndEnrolled).isFalse()
+        }
+
+    @Test
+    fun anySimSecure_disablesFaceAuthToNotCurrentlyRun() =
+        testScope.runTest {
+            faceAuthIsEnrolled()
+
+            createBiometricSettingsRepository()
+            val isFaceAuthCurrentlyAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed)
+
+            deviceIsInPostureThatSupportsFaceAuth()
+            doNotDisableKeyguardAuthFeatures()
+            faceAuthIsStrongBiometric()
+            faceAuthIsEnabledByBiometricManager()
+            mobileConnectionsRepository.isAnySimSecure.value = false
+
+            onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
+            onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+            assertThat(isFaceAuthCurrentlyAllowed).isTrue()
+
+            mobileConnectionsRepository.isAnySimSecure.value = true
+            assertThat(isFaceAuthCurrentlyAllowed).isFalse()
+        }
+
+    @Test
     fun biometricManagerControlsFaceAuthenticationEnabledStatus() =
         testScope.runTest {
             faceAuthIsEnrolled()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
similarity index 72%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
index 93f316e..9e5d3bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDisableFlagsLoggerTest.kt
@@ -29,15 +29,16 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() {
+class QSDisableFlagsLoggerTest : SysuiTestCase() {
 
-    private val buffer = LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java))
-        .create("buffer", 10)
-    private val disableFlagsLogger = DisableFlagsLogger(
-        listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
-        listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
-    )
-    private val logger = QSFragmentDisableFlagsLogger(buffer, disableFlagsLogger)
+    private val buffer =
+        LogBufferFactory(DumpManager(), mock(LogcatEchoTracker::class.java)).create("buffer", 10)
+    private val disableFlagsLogger =
+        DisableFlagsLogger(
+            listOf(DisableFlagsLogger.DisableFlag(0b001, 'A', 'a')),
+            listOf(DisableFlagsLogger.DisableFlag(0b001, 'B', 'b'))
+        )
+    private val logger = QSDisableFlagsLogger(buffer, disableFlagsLogger)
 
     @Test
     fun logDisableFlagChange_bufferHasStates() {
@@ -48,9 +49,8 @@
         val stringWriter = StringWriter()
         buffer.dump(PrintWriter(stringWriter), tailLength = 0)
         val actualString = stringWriter.toString()
-        val expectedLogString = disableFlagsLogger.getDisableFlagsString(
-            new = state, newAfterLocalModification = state
-        )
+        val expectedLogString =
+            disableFlagsLogger.getDisableFlagsString(new = state, newAfterLocalModification = state)
 
         assertThat(actualString).contains(expectedLogString)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
similarity index 67%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index c4c233c..d57765c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -1,15 +1,17 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open 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;
@@ -34,7 +36,6 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
-import android.app.Fragment;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -49,12 +50,12 @@
 
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.res.R;
-import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.media.controls.ui.MediaHost;
 import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.dagger.QSComponent;
 import com.android.systemui.qs.external.TileServiceRequestController;
 import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
@@ -66,8 +67,8 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.util.animation.UniqueObjectHostView;
 
 import org.junit.Before;
@@ -79,10 +80,9 @@
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
-public class QSFragmentTest extends SysuiBaseFragmentTest {
+public class QSImplTest extends SysuiTestCase {
 
-    @Mock private QSFragmentComponent.Factory mQsComponentFactory;
-    @Mock private QSFragmentComponent mQsFragmentComponent;
+    @Mock private QSComponent mQsComponent;
     @Mock private QSPanelController mQSPanelController;
     @Mock private MediaHost mQSMediaHost;
     @Mock private MediaHost mQQSMediaHost;
@@ -107,69 +107,54 @@
     @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
     @Mock private FooterActionsViewBinder mFooterActionsViewBinder;
     @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
-    @Mock private FeatureFlags mFeatureFlags;
-    private View mQsFragmentView;
+    @Mock private FeatureFlagsClassic mFeatureFlags;
+    private View mQsView;
 
-    public QSFragmentTest() {
-        super(QSFragment.class);
-    }
+    private QSImpl mUnderTest;
+
 
     @Before
     public void setup() {
-        injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+        mUnderTest = instantiate();
+
+        mUnderTest.onComponentCreated(mQsComponent, null);
     }
 
-    @Test
-    public void testListening() {
-        QSFragment qs = (QSFragment) mFragment;
-        mFragments.dispatchResume();
-        processAllMessages();
-
-        qs.setListening(true);
-        processAllMessages();
-
-        qs.setListening(false);
-        processAllMessages();
-    }
 
     @Test
     public void testSaveState() {
-        mFragments.dispatchResume();
-        processAllMessages();
+        mUnderTest.setListening(true);
+        mUnderTest.setExpanded(true);
+        mUnderTest.setQsVisible(true);
 
-        QSFragment qs = (QSFragment) mFragment;
-        qs.setListening(true);
-        qs.setExpanded(true);
-        qs.setQsVisible(true);
-        processAllMessages();
-        recreateFragment();
-        processAllMessages();
+        Bundle bundle = new Bundle();
+        mUnderTest.onSaveInstanceState(bundle);
 
-        // Get the reference to the new fragment.
-        qs = (QSFragment) mFragment;
-        assertTrue(qs.isListening());
-        assertTrue(qs.isExpanded());
-        assertTrue(qs.isQsVisible());
+        // Get a new instance
+        QSImpl other = instantiate();
+        other.onComponentCreated(mQsComponent, bundle);
+
+        assertTrue(other.isListening());
+        assertTrue(other.isExpanded());
+        assertTrue(other.isQsVisible());
     }
 
     @Test
     public void transitionToFullShade_smallScreen_alphaAlways1() {
-        QSFragment fragment = resumeAndGetFragment();
         setIsSmallScreen();
         setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
         boolean isTransitioningToFullShade = true;
         float transitionProgress = 0.5f;
         float squishinessFraction = 0.5f;
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(1f);
+        assertThat(mQsView.getAlpha()).isEqualTo(1f);
     }
 
     @Test
     public void transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator() {
-        QSFragment fragment = resumeAndGetFragment();
         setIsLargeScreen();
         setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
         boolean isTransitioningToFullShade = true;
@@ -177,43 +162,40 @@
         float squishinessFraction = 0.5f;
         when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f);
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
-        assertThat(mQsFragmentView.getAlpha())
-                .isEqualTo(123f);
+        assertThat(mQsView.getAlpha()).isEqualTo(123f);
     }
 
     @Test
     public void
             transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() {
-        QSFragment fragment = resumeAndGetFragment();
         setStatusBarCurrentAndUpcomingState(KEYGUARD);
         when(mQSPanelController.isBouncerInTransit()).thenReturn(false);
         boolean isTransitioningToFullShade = true;
         float transitionProgress = 0.5f;
         float squishinessFraction = 0.5f;
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+        assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
     }
 
     @Test
     public void
             transitionToFullShade_onKeyguard_bouncerActive_setsAlphaUsingBouncerInterpolator() {
-        QSFragment fragment = resumeAndGetFragment();
         setStatusBarCurrentAndUpcomingState(KEYGUARD);
         when(mQSPanelController.isBouncerInTransit()).thenReturn(true);
         boolean isTransitioningToFullShade = true;
         float transitionProgress = 0.5f;
         float squishinessFraction = 0.5f;
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
-        assertThat(mQsFragmentView.getAlpha())
+        assertThat(mQsView.getAlpha())
                 .isEqualTo(
                         BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(
                                 transitionProgress));
@@ -221,40 +203,37 @@
 
     @Test
     public void transitionToFullShade_inFullWidth_alwaysSetsAlphaTo1() {
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setIsNotificationPanelFullWidth(true);
+        mUnderTest.setIsNotificationPanelFullWidth(true);
 
         boolean isTransitioningToFullShade = true;
         float transitionProgress = 0.1f;
         float squishinessFraction = 0.5f;
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+        assertThat(mQsView.getAlpha()).isEqualTo(1);
 
         transitionProgress = 0.5f;
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+        assertThat(mQsView.getAlpha()).isEqualTo(1);
+        assertThat(mQsView.getAlpha()).isEqualTo(1);
 
         transitionProgress = 0.7f;
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
+        assertThat(mQsView.getAlpha()).isEqualTo(1);
     }
 
     @Test
     public void transitionToFullShade_setsSquishinessOnController() {
-        QSFragment fragment = resumeAndGetFragment();
         boolean isTransitioningToFullShade = true;
         float transitionProgress = 0.123f;
         float squishinessFraction = 0.456f;
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
-        verify(mQsFragmentComponent.getQSSquishinessController())
-                .setSquishiness(squishinessFraction);
+        verify(mQsComponent.getQSSquishinessController()).setSquishiness(squishinessFraction);
     }
 
     @Test
@@ -265,10 +244,9 @@
         float proposedTranslation = 456f;
         float squishinessFraction = 0.987f;
 
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
 
-        fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+        mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
                 squishinessFraction);
 
         verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -283,10 +261,9 @@
         float proposedTranslation = 456f;
         float squishinessFraction = 0.987f;
 
-        QSFragment fragment = resumeAndGetFragment();
         disableSplitShade();
 
-        fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+        mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
                 squishinessFraction);
 
         verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
@@ -295,7 +272,6 @@
 
     @Test
     public void setQsExpansion_inSplitShade_whenTransitioningToKeyguard_setsAlphaBasedOnShadeTransitionProgress() {
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
         when(mStatusBarStateController.getState()).thenReturn(SHADE);
         when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
@@ -303,24 +279,23 @@
         float transitionProgress = 0;
         float squishinessFraction = 0f;
 
-        fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
+        mUnderTest.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
                 squishinessFraction);
 
         // trigger alpha refresh with non-zero expansion and fraction values
-        fragment.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
+        mUnderTest.setQsExpansion(/* expansion= */ 1, /* panelExpansionFraction= */1,
                 /* proposedTranslation= */ 0, /* squishinessFraction= */ 1);
 
         // alpha should follow lockscreen to shade progress, not panel expansion fraction
-        assertThat(mQsFragmentView.getAlpha()).isEqualTo(transitionProgress);
+        assertThat(mQsView.getAlpha()).isEqualTo(transitionProgress);
     }
 
     @Test
     public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
-        QSFragment fragment = resumeAndGetFragment();
         disableSplitShade();
         when(mHeader.getHeight()).thenReturn(1234);
 
-        int height = fragment.getQsMinExpansionHeight();
+        int height = mUnderTest.getQsMinExpansionHeight();
 
         assertThat(height).isEqualTo(mHeader.getHeight());
     }
@@ -329,13 +304,12 @@
     public void getQsMinExpansionHeight_inSplitShade_returnsAbsoluteBottomOfQSContainer() {
         int top = 1234;
         int height = 9876;
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
-        setLocationOnScreen(mQsFragmentView, top);
-        when(mQsFragmentView.getHeight()).thenReturn(height);
+        setLocationOnScreen(mQsView, top);
+        when(mQsView.getHeight()).thenReturn(height);
 
         int expectedHeight = top + height;
-        assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+        assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
     }
 
     @Test
@@ -343,47 +317,43 @@
         int top = 1234;
         int height = 9876;
         float translationY = -600f;
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
-        setLocationOnScreen(mQsFragmentView, (int) (top + translationY));
-        when(mQsFragmentView.getHeight()).thenReturn(height);
-        when(mQsFragmentView.getTranslationY()).thenReturn(translationY);
+        setLocationOnScreen(mQsView, (int) (top + translationY));
+        when(mQsView.getHeight()).thenReturn(height);
+        when(mQsView.getTranslationY()).thenReturn(translationY);
 
         int expectedHeight = top + height;
-        assertThat(fragment.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
+        assertThat(mUnderTest.getQsMinExpansionHeight()).isEqualTo(expectedHeight);
     }
 
     @Test
     public void hideImmediately_notInSplitShade_movesViewUpByHeaderHeight() {
-        QSFragment fragment = resumeAndGetFragment();
         disableSplitShade();
         when(mHeader.getHeight()).thenReturn(555);
 
-        fragment.hideImmediately();
+        mUnderTest.hideImmediately();
 
-        assertThat(mQsFragmentView.getY()).isEqualTo(-mHeader.getHeight());
+        assertThat(mQsView.getY()).isEqualTo(-mHeader.getHeight());
     }
 
     @Test
     public void hideImmediately_inSplitShade_movesViewUpByQSAbsoluteBottom() {
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
         int top = 1234;
         int height = 9876;
-        setLocationOnScreen(mQsFragmentView, top);
-        when(mQsFragmentView.getHeight()).thenReturn(height);
+        setLocationOnScreen(mQsView, top);
+        when(mQsView.getHeight()).thenReturn(height);
 
-        fragment.hideImmediately();
+        mUnderTest.hideImmediately();
 
         int qsAbsoluteBottom = top + height;
-        assertThat(mQsFragmentView.getY()).isEqualTo(-qsAbsoluteBottom);
+        assertThat(mQsView.getY()).isEqualTo(-qsAbsoluteBottom);
     }
 
     @Test
     public void setCollapseExpandAction_passedToControllers() {
         Runnable action = () -> {};
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setCollapseExpandAction(action);
+        mUnderTest.setCollapseExpandAction(action);
 
         verify(mQSPanelController).setCollapseExpandAction(action);
         verify(mQuickQSPanelController).setCollapseExpandAction(action);
@@ -391,24 +361,21 @@
 
     @Test
     public void setOverScrollAmount_setsTranslationOnView() {
-        QSFragment fragment = resumeAndGetFragment();
+        mUnderTest.setOverScrollAmount(123);
 
-        fragment.setOverScrollAmount(123);
-
-        assertThat(mQsFragmentView.getTranslationY()).isEqualTo(123);
+        assertThat(mQsView.getTranslationY()).isEqualTo(123);
     }
 
     @Test
     public void setOverScrollAmount_beforeViewCreated_translationIsNotSet() {
-        QSFragment fragment = getFragment();
+        QSImpl other = instantiate();
+        other.setOverScrollAmount(123);
 
-        fragment.setOverScrollAmount(123);
-
-        assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0);
+        assertThat(mQsView.getTranslationY()).isEqualTo(0);
     }
 
     private Lifecycle.State getListeningAndVisibilityLifecycleState() {
-        return getFragment()
+        return mUnderTest
                 .getListeningAndVisibilityLifecycleOwner()
                 .getLifecycle()
                 .getCurrentState();
@@ -416,11 +383,10 @@
 
     @Test
     public void setListeningFalse_notVisible() {
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setQsVisible(false);
+        mUnderTest.setQsVisible(false);
         clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
 
-        fragment.setListening(false);
+        mUnderTest.setListening(false);
         verify(mQSContainerImplController).setListening(false);
         assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
         verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -428,11 +394,10 @@
 
     @Test
     public void setListeningTrue_notVisible() {
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setQsVisible(false);
+        mUnderTest.setQsVisible(false);
         clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
 
-        fragment.setListening(true);
+        mUnderTest.setListening(true);
         verify(mQSContainerImplController).setListening(false);
         assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED);
         verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -440,11 +405,10 @@
 
     @Test
     public void setListeningFalse_visible() {
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setQsVisible(true);
+        mUnderTest.setQsVisible(true);
         clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
 
-        fragment.setListening(false);
+        mUnderTest.setListening(false);
         verify(mQSContainerImplController).setListening(false);
         assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
         verify(mQSPanelController).setListening(eq(false), anyBoolean());
@@ -452,11 +416,10 @@
 
     @Test
     public void setListeningTrue_visible() {
-        QSFragment fragment = resumeAndGetFragment();
-        fragment.setQsVisible(true);
+        mUnderTest.setQsVisible(true);
         clearInvocations(mQSContainerImplController, mQSPanelController, mQSFooterActionController);
 
-        fragment.setListening(true);
+        mUnderTest.setListening(true);
         verify(mQSContainerImplController).setListening(true);
         assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED);
         verify(mQSPanelController).setListening(eq(true), anyBoolean());
@@ -464,31 +427,28 @@
 
     @Test
     public void passCorrectExpansionState_inSplitShade() {
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
         clearInvocations(mQSPanelController);
 
-        fragment.setExpanded(true);
+        mUnderTest.setExpanded(true);
         verify(mQSPanelController).setExpanded(true);
 
-        fragment.setExpanded(false);
+        mUnderTest.setExpanded(false);
         verify(mQSPanelController).setExpanded(false);
     }
 
     @Test
     public void startsListeningAfterStateChangeToExpanded_inSplitShade() {
-        QSFragment fragment = resumeAndGetFragment();
         enableSplitShade();
-        fragment.setQsVisible(true);
+        mUnderTest.setQsVisible(true);
         clearInvocations(mQSPanelController);
 
-        fragment.setExpanded(true);
+        mUnderTest.setExpanded(true);
         verify(mQSPanelController).setListening(true, true);
     }
 
     @Test
     public void testUpdateQSBounds_setMediaClipCorrectly() {
-        QSFragment fragment = resumeAndGetFragment();
         disableSplitShade();
 
         Rect mediaHostClip = new Rect();
@@ -497,7 +457,7 @@
         when(mQSPanelScrollView.getMeasuredHeight()).thenReturn(200);
         when(mQSMediaHost.getCurrentClipping()).thenReturn(mediaHostClip);
 
-        fragment.updateQsBounds();
+        mUnderTest.updateQsBounds();
 
         assertEquals(25, mediaHostClip.top);
         assertEquals(175, mediaHostClip.bottom);
@@ -505,17 +465,15 @@
 
     @Test
     public void testQsUpdatesQsAnimatorWithUpcomingState() {
-        QSFragment fragment = resumeAndGetFragment();
         setStatusBarCurrentAndUpcomingState(SHADE);
-        fragment.onUpcomingStateChanged(KEYGUARD);
+        mUnderTest.onUpcomingStateChanged(KEYGUARD);
 
         verify(mQSAnimator).setOnKeyguard(true);
     }
 
-    @Override
-    protected Fragment instantiate(Context context, String className, Bundle arguments) {
+    private QSImpl instantiate() {
         MockitoAnnotations.initMocks(this);
-        CommandQueue commandQueue = new CommandQueue(context, new FakeDisplayTracker(context));
+        CommandQueue commandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext));
 
         setupQsComponent();
         setUpViews();
@@ -523,9 +481,9 @@
         setUpMedia();
         setUpOther();
 
-        return new QSFragment(
+        return new QSImpl(
                 new RemoteInputQuickSettingsDisabler(
-                        context,
+                        mContext,
                         commandQueue,
                         new ResourcesSplitShadeStateController(),
                         mock(ConfigurationController.class)),
@@ -534,8 +492,7 @@
                 mQSMediaHost,
                 mQQSMediaHost,
                 mBypassController,
-                mQsComponentFactory,
-                mock(QSFragmentDisableFlagsLogger.class),
+                mock(QSDisableFlagsLogger.class),
                 mock(DumpManager.class),
                 mock(QSLogger.class),
                 mock(FooterActionsController.class),
@@ -561,12 +518,13 @@
     }
 
     private void setUpViews() {
-        mQsFragmentView = spy(new View(mContext));
-        when(mQsFragmentView.findViewById(R.id.expanded_qs_scroll_view))
+        mQsView = spy(new View(mContext));
+        when(mQsComponent.getRootView()).thenReturn(mQsView);
+        when(mQsView.findViewById(R.id.expanded_qs_scroll_view))
                 .thenReturn(mQSPanelScrollView);
-        when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader);
-        when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
-        when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer(
+        when(mQsView.findViewById(R.id.header)).thenReturn(mHeader);
+        when(mQsView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
+        when(mQsView.findViewById(R.id.qs_footer_actions)).thenAnswer(
                 invocation -> new FooterActionsViewBinder().create(mContext));
     }
 
@@ -597,37 +555,26 @@
             return realInflater.inflate(layoutRes, root, attachToRoot);
         }
 
-        return mQsFragmentView;
+        return mQsView;
     }
 
     private void setupQsComponent() {
-        when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
-        when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
-        when(mQsFragmentComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
-        when(mQsFragmentComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
-        when(mQsFragmentComponent.getQSContainerImplController())
+        when(mQsComponent.getQSPanelController()).thenReturn(mQSPanelController);
+        when(mQsComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
+        when(mQsComponent.getQSCustomizerController()).thenReturn(mQsCustomizerController);
+        when(mQsComponent.getQSContainerImplController())
                 .thenReturn(mQSContainerImplController);
-        when(mQsFragmentComponent.getQSFooter()).thenReturn(mFooter);
-        when(mQsFragmentComponent.getQSFooterActionController())
+        when(mQsComponent.getQSFooter()).thenReturn(mFooter);
+        when(mQsComponent.getQSFooterActionController())
                 .thenReturn(mQSFooterActionController);
-        when(mQsFragmentComponent.getQSAnimator()).thenReturn(mQSAnimator);
-        when(mQsFragmentComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
-    }
-
-    private QSFragment getFragment() {
-        return ((QSFragment) mFragment);
-    }
-
-    private QSFragment resumeAndGetFragment() {
-        mFragments.dispatchResume();
-        processAllMessages();
-        return getFragment();
+        when(mQsComponent.getQSAnimator()).thenReturn(mQSAnimator);
+        when(mQsComponent.getQSSquishinessController()).thenReturn(mSquishinessController);
     }
 
     private void setStatusBarCurrentAndUpcomingState(int statusBarState) {
         when(mStatusBarStateController.getState()).thenReturn(statusBarState);
         when(mStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(statusBarState);
-        getFragment().onStateChanged(statusBarState);
+        mUnderTest.onStateChanged(statusBarState);
     }
 
     private void enableSplitShade() {
@@ -639,7 +586,7 @@
     }
 
     private void setSplitShadeEnabled(boolean enabled) {
-        getFragment().setInSplitShade(enabled);
+        mUnderTest.setInSplitShade(enabled);
     }
 
     private void setLocationOnScreen(View view, int top) {
@@ -652,10 +599,10 @@
     }
 
     private void setIsLargeScreen() {
-        getFragment().setIsNotificationPanelFullWidth(false);
+        mUnderTest.setIsNotificationPanelFullWidth(false);
     }
 
     private void setIsSmallScreen() {
-        getFragment().setIsNotificationPanelFullWidth(true);
+        mUnderTest.setIsNotificationPanelFullWidth(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
index 9386d71..9a55f72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/AutoAddSettingsRepositoryTest.kt
@@ -23,15 +23,19 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -39,6 +43,20 @@
 @RunWith(AndroidJUnit4::class)
 class AutoAddSettingsRepositoryTest : SysuiTestCase() {
     private val secureSettings = FakeSettings()
+    private val userAutoAddRepositoryFactory =
+        object : UserAutoAddRepository.Factory {
+            override fun create(userId: Int): UserAutoAddRepository {
+                return UserAutoAddRepository(
+                    userId,
+                    secureSettings,
+                    logger,
+                    testScope.backgroundScope,
+                    testDispatcher,
+                )
+            }
+        }
+
+    @Mock private lateinit var logger: QSPipelineLogger
 
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
@@ -47,110 +65,37 @@
 
     @Before
     fun setUp() {
-        underTest =
-            AutoAddSettingRepository(
-                secureSettings,
-                testDispatcher,
-            )
+        MockitoAnnotations.initMocks(this)
+
+        underTest = AutoAddSettingRepository(userAutoAddRepositoryFactory)
     }
 
     @Test
-    fun nonExistentSetting_emptySet() =
-        testScope.runTest {
-            val specs by collectLastValue(underTest.autoAddedTiles(0))
-
-            assertThat(specs).isEmpty()
-        }
-
-    @Test
-    fun settingsChange_correctValues() =
-        testScope.runTest {
-            val userId = 0
-            val specs by collectLastValue(underTest.autoAddedTiles(userId))
-
-            val value = "a,custom(b/c)"
-            storeForUser(value, userId)
-
-            assertThat(specs).isEqualTo(value.toSet())
-
-            val newValue = "a"
-            storeForUser(newValue, userId)
-
-            assertThat(specs).isEqualTo(newValue.toSet())
-        }
-
-    @Test
     fun tilesForCorrectUsers() =
         testScope.runTest {
-            val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
-            val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
-
             val user0Tiles = "a"
             val user1Tiles = "custom(b/c)"
             storeForUser(user0Tiles, 0)
             storeForUser(user1Tiles, 1)
+            val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+            val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+            runCurrent()
 
-            assertThat(tilesFromUser0).isEqualTo(user0Tiles.toSet())
-            assertThat(tilesFromUser1).isEqualTo(user1Tiles.toSet())
-        }
-
-    @Test
-    fun noInvalidTileSpecs() =
-        testScope.runTest {
-            val userId = 0
-            val tiles by collectLastValue(underTest.autoAddedTiles(userId))
-
-            val specs = "d,custom(bad)"
-            storeForUser(specs, userId)
-
-            assertThat(tiles).isEqualTo("d".toSet())
-        }
-
-    @Test
-    fun markAdded() =
-        testScope.runTest {
-            val userId = 0
-            val specs = mutableSetOf(TileSpec.create("a"))
-            underTest.markTileAdded(userId, TileSpec.create("a"))
-
-            assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
-
-            specs.add(TileSpec.create("b"))
-            underTest.markTileAdded(userId, TileSpec.create("b"))
-
-            assertThat(loadForUser(userId).toSet()).containsExactlyElementsIn(specs)
+            assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTilesSet())
+            assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTilesSet())
         }
 
     @Test
     fun markAdded_multipleUsers() =
         testScope.runTest {
+            val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+            val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+            runCurrent()
+
             underTest.markTileAdded(userId = 1, TileSpec.create("a"))
 
-            assertThat(loadForUser(0).toSet()).isEmpty()
-            assertThat(loadForUser(1).toSet())
-                .containsExactlyElementsIn(setOf(TileSpec.create("a")))
-        }
-
-    @Test
-    fun markAdded_Invalid_noop() =
-        testScope.runTest {
-            val userId = 0
-            underTest.markTileAdded(userId, TileSpec.Invalid)
-
-            assertThat(loadForUser(userId).toSet()).isEmpty()
-        }
-
-    @Test
-    fun unmarkAdded() =
-        testScope.runTest {
-            val userId = 0
-            val specs = "a,custom(b/c)"
-            storeForUser(specs, userId)
-
-            underTest.unmarkTileAdded(userId, TileSpec.create("a"))
-
-            assertThat(loadForUser(userId).toSet())
-                .containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+            assertThat(tilesFromUser0).isEmpty()
+            assertThat(tilesFromUser1).containsExactlyElementsIn(setOf(TileSpec.create("a")))
         }
 
     @Test
@@ -159,33 +104,23 @@
             val specs = "a,b"
             storeForUser(specs, 0)
             storeForUser(specs, 1)
+            val tilesFromUser0 by collectLastValue(underTest.autoAddedTiles(0))
+            val tilesFromUser1 by collectLastValue(underTest.autoAddedTiles(1))
+            runCurrent()
 
             underTest.unmarkTileAdded(1, TileSpec.create("a"))
 
-            assertThat(loadForUser(0).toSet()).isEqualTo(specs.toSet())
-            assertThat(loadForUser(1).toSet()).isEqualTo(setOf(TileSpec.create("b")))
+            assertThat(tilesFromUser0).isEqualTo(specs.toTilesSet())
+            assertThat(tilesFromUser1).isEqualTo(setOf(TileSpec.create("b")))
         }
 
     private fun storeForUser(specs: String, userId: Int) {
         secureSettings.putStringForUser(SETTING, specs, userId)
     }
 
-    private fun loadForUser(userId: Int): String {
-        return secureSettings.getStringForUser(SETTING, userId) ?: ""
-    }
-
     companion object {
         private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
-        private const val DELIMITER = ","
 
-        fun Set<TileSpec>.toSeparatedString() = joinToString(DELIMITER, transform = TileSpec::spec)
-
-        fun String.toSet(): Set<TileSpec> {
-            return if (isNullOrBlank()) {
-                emptySet()
-            } else {
-                split(DELIMITER).map(TileSpec::create).toSet()
-            }
-        }
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
new file mode 100644
index 0000000..dc09a33
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/QSSettingsRestoredBroadcastRepositoryTest.kt
@@ -0,0 +1,220 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.content.Intent
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@RoboPilotTest
+class QSSettingsRestoredBroadcastRepositoryTest : SysuiTestCase() {
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+
+    @Mock private lateinit var pipelineLogger: QSPipelineLogger
+
+    private lateinit var underTest: QSSettingsRestoredBroadcastRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            QSSettingsRestoredBroadcastRepository(
+                fakeBroadcastDispatcher,
+                pipelineLogger,
+                testScope.backgroundScope,
+                dispatcher,
+            )
+    }
+
+    @Test
+    fun restoreDataAfterBothIntents_tilesRestoredFirst() =
+        testScope.runTest {
+            runCurrent()
+            val restoreData by collectLastValue(underTest.restoreData)
+            val user = 0
+
+            val tilesIntent =
+                createRestoreIntent(
+                    RestoreType.TILES,
+                    CURRENT_TILES,
+                    RESTORED_TILES,
+                )
+
+            val autoAddIntent =
+                createRestoreIntent(
+                    RestoreType.AUTOADD,
+                    CURRENT_AUTO_ADDED_TILES,
+                    RESTORED_AUTO_ADDED_TILES,
+                )
+
+            sendIntentForUser(tilesIntent, user)
+
+            // No restore data yet as we are missing one of the broadcasts
+            assertThat(restoreData).isNull()
+
+            // After the second event, we see the corresponding restore
+            sendIntentForUser(autoAddIntent, user)
+
+            with(restoreData!!) {
+                assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+                assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+                assertThat(userId).isEqualTo(user)
+            }
+        }
+
+    @Test
+    fun restoreDataAfterBothIntents_autoAddRestoredFirst() =
+        testScope.runTest {
+            runCurrent()
+            val restoreData by collectLastValue(underTest.restoreData)
+            val user = 0
+
+            val tilesIntent =
+                createRestoreIntent(
+                    RestoreType.TILES,
+                    CURRENT_TILES,
+                    RESTORED_TILES,
+                )
+
+            val autoAddIntent =
+                createRestoreIntent(
+                    RestoreType.AUTOADD,
+                    CURRENT_AUTO_ADDED_TILES,
+                    RESTORED_AUTO_ADDED_TILES,
+                )
+
+            sendIntentForUser(autoAddIntent, user)
+
+            // No restore data yet as we are missing one of the broadcasts
+            assertThat(restoreData).isNull()
+
+            // After the second event, we see the corresponding restore
+            sendIntentForUser(tilesIntent, user)
+
+            with(restoreData!!) {
+                assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+                assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+                assertThat(userId).isEqualTo(user)
+            }
+        }
+
+    @Test
+    fun interleavedBroadcastsFromDifferentUsers_onlysendDataForCorrectUser() =
+        testScope.runTest {
+            runCurrent()
+            val restoreData by collectLastValue(underTest.restoreData)
+
+            val user0 = 0
+            val user10 = 10
+
+            val currentTiles10 = "z,y,x"
+            val restoredTiles10 = "x"
+            val currentAutoAdded10 = "f"
+            val restoredAutoAdded10 = "f,g"
+
+            val tilesIntent0 =
+                createRestoreIntent(
+                    RestoreType.TILES,
+                    CURRENT_TILES,
+                    RESTORED_TILES,
+                )
+            val autoAddIntent0 =
+                createRestoreIntent(
+                    RestoreType.AUTOADD,
+                    CURRENT_AUTO_ADDED_TILES,
+                    RESTORED_AUTO_ADDED_TILES,
+                )
+            val tilesIntent10 =
+                createRestoreIntent(
+                    RestoreType.TILES,
+                    currentTiles10,
+                    restoredTiles10,
+                )
+            val autoAddIntent10 =
+                createRestoreIntent(
+                    RestoreType.AUTOADD,
+                    currentAutoAdded10,
+                    restoredAutoAdded10,
+                )
+
+            sendIntentForUser(tilesIntent0, user0)
+            sendIntentForUser(autoAddIntent10, user10)
+            assertThat(restoreData).isNull()
+
+            sendIntentForUser(tilesIntent10, user10)
+
+            with(restoreData!!) {
+                assertThat(restoredTiles).isEqualTo(restoredTiles10.toTilesList())
+                assertThat(restoredAutoAddedTiles).isEqualTo(restoredAutoAdded10.toTilesSet())
+                assertThat(userId).isEqualTo(user10)
+            }
+
+            sendIntentForUser(autoAddIntent0, user0)
+
+            with(restoreData!!) {
+                assertThat(restoredTiles).isEqualTo(RESTORED_TILES.toTilesList())
+                assertThat(restoredAutoAddedTiles).isEqualTo(RESTORED_AUTO_ADDED_TILES.toTilesSet())
+                assertThat(userId).isEqualTo(user0)
+            }
+        }
+
+    private fun sendIntentForUser(intent: Intent, userId: Int) {
+        fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            intent,
+            FakeBroadcastDispatcher.fakePendingResultForUser(userId)
+        )
+    }
+
+    companion object {
+        private const val CURRENT_TILES = "a,b,c,d"
+        private const val RESTORED_TILES = "b,a,c"
+        private const val CURRENT_AUTO_ADDED_TILES = "d"
+        private const val RESTORED_AUTO_ADDED_TILES = "e"
+
+        private fun createRestoreIntent(
+            type: RestoreType,
+            previousValue: String,
+            restoredValue: String,
+        ): Intent {
+            val setting =
+                when (type) {
+                    RestoreType.TILES -> Settings.Secure.QS_TILES
+                    RestoreType.AUTOADD -> Settings.Secure.QS_AUTO_ADDED_TILES
+                }
+            return Intent(Intent.ACTION_SETTING_RESTORED)
+                .putExtra(Intent.EXTRA_SETTING_NAME, setting)
+                .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue)
+                .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue)
+        }
+
+        private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+
+        private enum class RestoreType {
+            TILES,
+            AUTOADD,
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 1c28e4c..08adebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -23,14 +23,12 @@
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.retail.data.repository.FakeRetailModeRepository
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -49,9 +47,28 @@
 
     private lateinit var secureSettings: FakeSettings
     private lateinit var retailModeRepository: FakeRetailModeRepository
+    private val defaultTilesRepository =
+        object : DefaultTilesRepository {
+            override val defaultTiles: List<TileSpec>
+                get() = DEFAULT_TILES.toTileSpecs()
+        }
 
     @Mock private lateinit var logger: QSPipelineLogger
 
+    private val userTileSpecRepositoryFactory =
+        object : UserTileSpecRepository.Factory {
+            override fun create(userId: Int): UserTileSpecRepository {
+                return UserTileSpecRepository(
+                    userId,
+                    defaultTilesRepository,
+                    secureSettings,
+                    logger,
+                    testScope.backgroundScope,
+                    testDispatcher,
+                )
+            }
+        }
+
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
@@ -66,293 +83,85 @@
         retailModeRepository.setRetailMode(false)
 
         with(context.orCreateTestableResources) {
-            addOverride(R.string.quick_settings_tiles_default, DEFAULT_TILES)
             addOverride(R.string.quick_settings_tiles_retail_mode, RETAIL_TILES)
         }
 
         underTest =
             TileSpecSettingsRepository(
-                secureSettings,
                 context.resources,
                 logger,
                 retailModeRepository,
-                testDispatcher,
+                userTileSpecRepositoryFactory
             )
     }
 
     @Test
-    fun emptySetting_usesDefaultValue() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-            assertThat(tiles).isEqualTo(getDefaultTileSpecs())
-        }
-
-    @Test
-    fun changeInSettings_changesValue() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("a", 0)
-            assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
-
-            storeTilesForUser("a,custom(b/c)", 0)
-            assertThat(tiles)
-                .isEqualTo(listOf(TileSpec.create("a"), TileSpec.create("custom(b/c)")))
-        }
-
-    @Test
     fun tilesForCorrectUsers() =
         testScope.runTest {
-            val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
-            val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
-
             val user0Tiles = "a"
             val user1Tiles = "custom(b/c)"
             storeTilesForUser(user0Tiles, 0)
             storeTilesForUser(user1Tiles, 1)
 
+            val tilesFromUser0 by collectLastValue(underTest.tilesSpecs(0))
+            val tilesFromUser1 by collectLastValue(underTest.tilesSpecs(1))
+
             assertThat(tilesFromUser0).isEqualTo(user0Tiles.toTileSpecs())
             assertThat(tilesFromUser1).isEqualTo(user1Tiles.toTileSpecs())
         }
 
     @Test
-    fun invalidTilesAreNotPresent() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "d,custom(bad)"
-            storeTilesForUser(specs, 0)
-
-            assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
-        }
-
-    @Test
-    fun noValidTiles_defaultSet() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("custom(bad),custom()", 0)
-
-            assertThat(tiles).isEqualTo(getDefaultTileSpecs())
-        }
-
-    @Test
-    fun addTileAtEnd() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("a", 0)
-
-            underTest.addTile(userId = 0, TileSpec.create("b"))
-
-            val expected = "a,b"
-            assertThat(loadTilesForUser(0)).isEqualTo(expected)
-            assertThat(tiles).isEqualTo(expected.toTileSpecs())
-        }
-
-    @Test
-    fun addTileAtPosition() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("a,custom(b/c)", 0)
-
-            underTest.addTile(userId = 0, TileSpec.create("d"), position = 1)
-
-            val expected = "a,d,custom(b/c)"
-            assertThat(loadTilesForUser(0)).isEqualTo(expected)
-            assertThat(tiles).isEqualTo(expected.toTileSpecs())
-        }
-
-    @Test
-    fun addInvalidTile_noop() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,custom(b/c)"
-            storeTilesForUser(specs, 0)
-
-            underTest.addTile(userId = 0, TileSpec.Invalid)
-
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
-            assertThat(tiles).isEqualTo(specs.toTileSpecs())
-        }
-
-    @Test
-    fun addTileAtPosition_tooLarge_addedAtEnd() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,custom(b/c)"
-            storeTilesForUser(specs, 0)
-
-            underTest.addTile(userId = 0, TileSpec.create("d"), position = 100)
-
-            val expected = "a,custom(b/c),d"
-            assertThat(loadTilesForUser(0)).isEqualTo(expected)
-            assertThat(tiles).isEqualTo(expected.toTileSpecs())
-        }
-
-    @Test
     fun addTileForOtherUser_addedInThatUser() =
         testScope.runTest {
-            val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
-            val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
-
             storeTilesForUser("a", 0)
             storeTilesForUser("b", 1)
+            val tilesUser0 by collectLastValue(underTest.tilesSpecs(0))
+            val tilesUser1 by collectLastValue(underTest.tilesSpecs(1))
+            runCurrent()
 
             underTest.addTile(userId = 1, TileSpec.create("c"))
 
-            assertThat(loadTilesForUser(0)).isEqualTo("a")
             assertThat(tilesUser0).isEqualTo("a".toTileSpecs())
-            assertThat(loadTilesForUser(1)).isEqualTo("b,c")
+            assertThat(loadTilesForUser(0)).isEqualTo("a")
             assertThat(tilesUser1).isEqualTo("b,c".toTileSpecs())
-        }
-
-    @Test
-    fun removeTiles() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("a,b", 0)
-
-            underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
-
-            assertThat(loadTilesForUser(0)).isEqualTo("b")
-            assertThat(tiles).isEqualTo("b".toTileSpecs())
-        }
-
-    @Test
-    fun removeTilesNotThere_noop() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,b"
-            storeTilesForUser(specs, 0)
-
-            underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
-
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
-            assertThat(tiles).isEqualTo(specs.toTileSpecs())
-        }
-
-    @Test
-    fun removeInvalidTile_noop() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,b"
-            storeTilesForUser(specs, 0)
-
-            underTest.removeTiles(userId = 0, listOf(TileSpec.Invalid))
-
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
-            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTilesForUser(1)).isEqualTo("b,c")
         }
 
     @Test
     fun removeTileFromSecondaryUser_removedOnlyInCorrectUser() =
         testScope.runTest {
-            val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
-            val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
             val specs = "a,b"
             storeTilesForUser(specs, 0)
             storeTilesForUser(specs, 1)
+            val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+            val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+            runCurrent()
 
             underTest.removeTiles(userId = 1, listOf(TileSpec.create("a")))
 
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
             assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
-            assertThat(loadTilesForUser(1)).isEqualTo("b")
+            assertThat(loadTilesForUser(0)).isEqualTo(specs)
             assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
-        }
-
-    @Test
-    fun removeMultipleTiles() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            storeTilesForUser("a,b,c,d", 0)
-
-            underTest.removeTiles(userId = 0, listOf(TileSpec.create("a"), TileSpec.create("c")))
-
-            assertThat(loadTilesForUser(0)).isEqualTo("b,d")
-            assertThat(tiles).isEqualTo("b,d".toTileSpecs())
-        }
-
-    @Test
-    fun changeTiles() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,custom(b/c)"
-
-            underTest.setTiles(userId = 0, specs.toTileSpecs())
-
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
-            assertThat(tiles).isEqualTo(specs.toTileSpecs())
-        }
-
-    @Test
-    fun changeTiles_ignoresInvalid() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,custom(b/c)"
-
-            underTest.setTiles(userId = 0, listOf(TileSpec.Invalid) + specs.toTileSpecs())
-
-            assertThat(loadTilesForUser(0)).isEqualTo(specs)
-            assertThat(tiles).isEqualTo(specs.toTileSpecs())
-        }
-
-    @Test
-    fun changeTiles_empty_noChanges() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            underTest.setTiles(userId = 0, emptyList())
-
-            assertThat(loadTilesForUser(0)).isNull()
-            assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+            assertThat(loadTilesForUser(1)).isEqualTo("b")
         }
 
     @Test
     fun changeTiles_forCorrectUser() =
         testScope.runTest {
-            val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
-            val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
-
             val specs = "a"
             storeTilesForUser(specs, 0)
             storeTilesForUser(specs, 1)
+            val user0Tiles by collectLastValue(underTest.tilesSpecs(0))
+            val user1Tiles by collectLastValue(underTest.tilesSpecs(1))
+            runCurrent()
 
             underTest.setTiles(userId = 1, "b".toTileSpecs())
 
-            assertThat(loadTilesForUser(0)).isEqualTo("a")
             assertThat(user0Tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTilesForUser(0)).isEqualTo("a")
 
-            assertThat(loadTilesForUser(1)).isEqualTo("b")
             assertThat(user1Tiles).isEqualTo("b".toTileSpecs())
-        }
-
-    @Test
-    fun multipleConcurrentRemovals_bothRemoved() =
-        testScope.runTest {
-            val tiles by collectLastValue(underTest.tilesSpecs(0))
-
-            val specs = "a,b,c"
-            storeTilesForUser(specs, 0)
-
-            coroutineScope {
-                underTest.removeTiles(userId = 0, listOf(TileSpec.create("c")))
-                underTest.removeTiles(userId = 0, listOf(TileSpec.create("a")))
-            }
-
-            assertThat(loadTilesForUser(0)).isEqualTo("b")
-            assertThat(tiles).isEqualTo("b".toTileSpecs())
+            assertThat(loadTilesForUser(1)).isEqualTo("b")
         }
 
     @Test
@@ -361,6 +170,7 @@
             retailModeRepository.setRetailMode(true)
 
             val tiles by collectLastValue(underTest.tilesSpecs(0))
+            runCurrent()
 
             assertThat(tiles).isEqualTo(RETAIL_TILES.toTileSpecs())
         }
@@ -369,25 +179,13 @@
     fun retailMode_cannotModifyTiles() =
         testScope.runTest {
             retailModeRepository.setRetailMode(true)
-
-            underTest.setTiles(0, DEFAULT_TILES.toTileSpecs())
-
-            assertThat(loadTilesForUser(0)).isNull()
-        }
-
-    @Test
-    fun emptyTilesReplacedByDefaultInSettings() =
-        testScope.runTest {
             val tiles by collectLastValue(underTest.tilesSpecs(0))
             runCurrent()
 
-            assertThat(loadTilesForUser(0))
-                .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
-        }
+            underTest.setTiles(0, listOf(TileSpec.create("a")))
 
-    private fun getDefaultTileSpecs(): List<TileSpec> {
-        return QSHost.getDefaultSpecs(context.resources).map(TileSpec::create)
-    }
+            assertThat(loadTilesForUser(0)).isEqualTo(DEFAULT_TILES)
+        }
 
     private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
         secureSettings.putStringForUser(SETTING, specs, forUser)
@@ -403,8 +201,6 @@
         private const val RETAIL_TILES = "d"
         private const val SETTING = Settings.Secure.QS_TILES
 
-        private fun String.toTileSpecs(): List<TileSpec> {
-            return split(",").map(TileSpec::create)
-        }
+        private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
new file mode 100644
index 0000000..2087623
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/TilesSettingConverterTest.kt
@@ -0,0 +1,100 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TilesSettingConverterTest : SysuiTestCase() {
+
+    @Test
+    fun toTilesList_correctContentAndOrdering() {
+        val specString =
+            listOf(
+                    "c",
+                    "b",
+                    "custom(x/y)",
+                    "d",
+                )
+                .joinToString(DELIMITER)
+
+        val expected =
+            listOf(
+                TileSpec.create("c"),
+                TileSpec.create("b"),
+                TileSpec.create("custom(x/y)"),
+                TileSpec.create("d"),
+            )
+
+        assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+    }
+
+    @Test
+    fun toTilesList_removesInvalid() {
+        val specString =
+            listOf(
+                    "a",
+                    "",
+                    "b",
+                )
+                .joinToString(DELIMITER)
+        assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+        val expected =
+            listOf(
+                TileSpec.create("a"),
+                TileSpec.create("b"),
+            )
+        assertThat(TilesSettingConverter.toTilesList(specString)).isEqualTo(expected)
+    }
+
+    @Test
+    fun toTilesSet_correctContent() {
+        val specString =
+            listOf(
+                    "c",
+                    "b",
+                    "custom(x/y)",
+                    "d",
+                )
+                .joinToString(DELIMITER)
+
+        val expected =
+            setOf(
+                TileSpec.create("c"),
+                TileSpec.create("b"),
+                TileSpec.create("custom(x/y)"),
+                TileSpec.create("d"),
+            )
+
+        assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+    }
+
+    @Test
+    fun toTilesSet_removesInvalid() {
+        val specString =
+            listOf(
+                    "a",
+                    "",
+                    "b",
+                )
+                .joinToString(DELIMITER)
+        assertThat(TileSpec.create("")).isEqualTo(TileSpec.Invalid)
+        val expected =
+            setOf(
+                TileSpec.create("a"),
+                TileSpec.create("b"),
+            )
+        assertThat(TilesSettingConverter.toTilesSet(specString)).isEqualTo(expected)
+    }
+
+    companion object {
+        private const val DELIMITER = ","
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
new file mode 100644
index 0000000..81fd72b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepositoryTest.kt
@@ -0,0 +1,160 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RoboPilotTest
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserAutoAddRepositoryTest : SysuiTestCase() {
+    private val secureSettings = FakeSettings()
+
+    @Mock private lateinit var logger: QSPipelineLogger
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    private lateinit var underTest: UserAutoAddRepository
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            UserAutoAddRepository(
+                USER,
+                secureSettings,
+                logger,
+                testScope.backgroundScope,
+                testDispatcher,
+            )
+    }
+
+    @Test
+    fun nonExistentSetting_emptySet() =
+        testScope.runTest {
+            val specs by collectLastValue(underTest.autoAdded())
+
+            assertThat(specs).isEmpty()
+        }
+
+    @Test
+    fun settingsChange_noChanges() =
+        testScope.runTest {
+            val value = "a,custom(b/c)"
+            store(value)
+            val specs by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            assertThat(specs).isEqualTo(value.toTilesSet())
+
+            val newValue = "a"
+            store(newValue)
+
+            assertThat(specs).isEqualTo(value.toTilesSet())
+        }
+
+    @Test
+    fun noInvalidTileSpecs() =
+        testScope.runTest {
+            val specs = "d,custom(bad)"
+            store(specs)
+            val tiles by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            assertThat(tiles).isEqualTo("d".toTilesSet())
+        }
+
+    @Test
+    fun markAdded() =
+        testScope.runTest {
+            val specs = mutableSetOf(TileSpec.create("a"))
+            val autoAdded by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            underTest.markTileAdded(TileSpec.create("a"))
+
+            assertThat(autoAdded).containsExactlyElementsIn(specs)
+
+            specs.add(TileSpec.create("b"))
+            underTest.markTileAdded(TileSpec.create("b"))
+
+            assertThat(autoAdded).containsExactlyElementsIn(specs)
+        }
+
+    @Test
+    fun markAdded_Invalid_noop() =
+        testScope.runTest {
+            val autoAdded by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            underTest.markTileAdded(TileSpec.Invalid)
+
+            Truth.assertThat(autoAdded).isEmpty()
+        }
+
+    @Test
+    fun unmarkAdded() =
+        testScope.runTest {
+            val specs = "a,custom(b/c)"
+            store(specs)
+            val autoAdded by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            underTest.unmarkTileAdded(TileSpec.create("a"))
+
+            assertThat(autoAdded).containsExactlyElementsIn(setOf(TileSpec.create("custom(b/c)")))
+        }
+
+    @Test
+    fun restore_addsRestoredTiles() =
+        testScope.runTest {
+            val specs = "a,b"
+            val restored = "b,c"
+            store(specs)
+            val autoAdded by collectLastValue(underTest.autoAdded())
+            runCurrent()
+
+            val restoreData =
+                RestoreData(
+                    emptyList(),
+                    restored.toTilesSet(),
+                    USER,
+                )
+            underTest.reconcileRestore(restoreData)
+
+            assertThat(autoAdded).containsExactlyElementsIn("a,b,c".toTilesSet())
+        }
+
+    private fun store(specs: String) {
+        secureSettings.putStringForUser(SETTING, specs, USER)
+    }
+
+    companion object {
+        private const val USER = 10
+        private const val SETTING = Settings.Secure.QS_AUTO_ADDED_TILES
+
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
new file mode 100644
index 0000000..389580c1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -0,0 +1,351 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
+import com.android.systemui.util.settings.FakeSettings
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+class UserTileSpecRepositoryTest : SysuiTestCase() {
+    private val secureSettings = FakeSettings()
+    private val defaultTilesRepository =
+        object : DefaultTilesRepository {
+            override val defaultTiles: List<TileSpec>
+                get() = DEFAULT_TILES.toTileSpecs()
+        }
+
+    @Mock private lateinit var logger: QSPipelineLogger
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    private lateinit var underTest: UserTileSpecRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            UserTileSpecRepository(
+                USER,
+                defaultTilesRepository,
+                secureSettings,
+                logger,
+                testScope.backgroundScope,
+                testDispatcher,
+            )
+    }
+
+    @Test
+    fun emptySetting_usesDefaultValue() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles())
+            assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+        }
+
+    @Test
+    fun changeInSettings_valueDoesntChange() =
+        testScope.runTest {
+            storeTiles("a")
+            val tiles by collectLastValue(underTest.tiles())
+
+            assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+
+            storeTiles("a,custom(b/c)")
+            assertThat(tiles).isEqualTo(listOf(TileSpec.create("a")))
+        }
+
+    @Test
+    fun changeInSettings_settingIsRestored() =
+        testScope.runTest {
+            storeTiles("a")
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            storeTiles("a,custom(b/c)")
+            assertThat(loadTiles()).isEqualTo("a")
+        }
+
+    @Test
+    fun invalidTilesAreNotPresent() =
+        testScope.runTest {
+            val specs = "d,custom(bad)"
+            storeTiles(specs)
+
+            val tiles by collectLastValue(underTest.tiles())
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs().filter { it != TileSpec.Invalid })
+        }
+
+    @Test
+    fun noValidTiles_defaultSet() =
+        testScope.runTest {
+            storeTiles("custom(bad),custom()")
+
+            val tiles by collectLastValue(underTest.tiles())
+
+            assertThat(tiles).isEqualTo(getDefaultTileSpecs())
+        }
+
+    /*
+     * Following tests are for the possible actions that can be performed to the list of tiles.
+     * In general, the tests follow this scheme:
+     *
+     * 1. Set starting tiles in Settings
+     * 2. Start collection of flows
+     * 3. Call `runCurrent` so all collectors are started (side effects)
+     * 4. Perform operation
+     * 5. Check that the flow contains the right value
+     * 6. Check that settings contains the right value.
+     */
+
+    @Test
+    fun addTileAtEnd() =
+        testScope.runTest {
+            storeTiles("a")
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.addTile(TileSpec.create("b"))
+
+            val expected = "a,b"
+            assertThat(tiles).isEqualTo(expected.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(expected)
+        }
+
+    @Test
+    fun addTileAtPosition() =
+        testScope.runTest {
+            storeTiles("a,custom(b/c)")
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.addTile(TileSpec.create("d"), position = 1)
+
+            val expected = "a,d,custom(b/c)"
+            assertThat(tiles).isEqualTo(expected.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(expected)
+        }
+
+    @Test
+    fun addInvalidTile_noop() =
+        testScope.runTest {
+            val specs = "a,custom(b/c)"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.addTile(TileSpec.Invalid)
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun addTileAtPosition_tooLarge_addedAtEnd() =
+        testScope.runTest {
+            val specs = "a,custom(b/c)"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.addTile(TileSpec.create("d"), position = 100)
+
+            val expected = "a,custom(b/c),d"
+            assertThat(tiles).isEqualTo(expected.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(expected)
+        }
+
+    @Test
+    fun removeTiles() =
+        testScope.runTest {
+            storeTiles("a,b")
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.removeTiles(listOf(TileSpec.create("a")))
+
+            assertThat(tiles).isEqualTo("b".toTileSpecs())
+            assertThat(loadTiles()).isEqualTo("b")
+        }
+
+    @Test
+    fun removeTilesNotThere_noop() =
+        testScope.runTest {
+            val specs = "a,b"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.removeTiles(listOf(TileSpec.create("c")))
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun removeInvalidTile_noop() =
+        testScope.runTest {
+            val specs = "a,b"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.removeTiles(listOf(TileSpec.Invalid))
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun removeMultipleTiles() =
+        testScope.runTest {
+            storeTiles("a,b,c,d")
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.removeTiles(listOf(TileSpec.create("a"), TileSpec.create("c")))
+
+            assertThat(tiles).isEqualTo("b,d".toTileSpecs())
+            assertThat(loadTiles()).isEqualTo("b,d")
+        }
+
+    @Test
+    fun changeTiles() =
+        testScope.runTest {
+            val specs = "a,custom(b/c)"
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.setTiles(specs.toTileSpecs())
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun changeTiles_ignoresInvalid() =
+        testScope.runTest {
+            val specs = "a,custom(b/c)"
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.setTiles(listOf(TileSpec.Invalid) + specs.toTileSpecs())
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun changeTiles_empty_noChanges() =
+        testScope.runTest {
+            val specs = "a,b,c,d"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            underTest.setTiles(emptyList())
+
+            assertThat(tiles).isEqualTo(specs.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(specs)
+        }
+
+    @Test
+    fun multipleConcurrentRemovals_bothRemoved() =
+        testScope.runTest {
+            val specs = "a,b,c"
+            storeTiles(specs)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            coroutineScope {
+                underTest.removeTiles(listOf(TileSpec.create("c")))
+                underTest.removeTiles(listOf(TileSpec.create("a")))
+            }
+
+            assertThat(tiles).isEqualTo("b".toTileSpecs())
+            assertThat(loadTiles()).isEqualTo("b")
+        }
+
+    @Test
+    fun emptyTilesReplacedByDefaultInSettings() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            assertThat(loadTiles())
+                .isEqualTo(getDefaultTileSpecs().map { it.spec }.joinToString(","))
+        }
+
+    @Test
+    fun restoreDataIsProperlyReconciled() =
+        testScope.runTest {
+            // Tile b was just auto-added, so we should re-add it in position 1
+            // Tile e was auto-added before, but the user had removed it (not in the restored set).
+            // It should not be re-added
+            val specsBeforeRestore = "a,b,c,d,e"
+            val restoredSpecs = "a,c,d,f"
+            val autoAddedBeforeRestore = "b,d"
+            val restoredAutoAdded = "d,e"
+
+            storeTiles(specsBeforeRestore)
+            val tiles by collectLastValue(underTest.tiles())
+            runCurrent()
+
+            val restoreData =
+                RestoreData(
+                    restoredSpecs.toTileSpecs(),
+                    restoredAutoAdded.toTilesSet(),
+                    USER,
+                )
+            underTest.reconcileRestore(restoreData, autoAddedBeforeRestore.toTilesSet())
+            runCurrent()
+
+            val expected = "a,b,c,d,f"
+            assertThat(tiles).isEqualTo(expected.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(expected)
+        }
+
+    private fun getDefaultTileSpecs(): List<TileSpec> {
+        return defaultTilesRepository.defaultTiles
+    }
+
+    private fun TestScope.storeTiles(specs: String) {
+        secureSettings.putStringForUser(SETTING, specs, USER)
+        runCurrent()
+    }
+
+    private fun loadTiles(): String? {
+        return secureSettings.getStringForUser(SETTING, USER)
+    }
+
+    companion object {
+        private const val USER = 10
+        private const val DEFAULT_TILES = "a,b,c"
+        private const val SETTING = Settings.Secure.QS_TILES
+
+        private fun String.toTileSpecs() = TilesSettingConverter.toTilesList(this)
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
new file mode 100644
index 0000000..5630b9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/RestoreReconciliationInteractorTest.kt
@@ -0,0 +1,94 @@
+package com.android.systemui.qs.pipeline.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import com.android.systemui.qs.pipeline.data.repository.FakeAutoAddRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeQSSettingsRestoredRepository
+import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository
+import com.android.systemui.qs.pipeline.data.repository.TilesSettingConverter
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@RoboPilotTest
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class RestoreReconciliationInteractorTest : SysuiTestCase() {
+
+    private val tileSpecRepository = FakeTileSpecRepository()
+    private val autoAddRepository = FakeAutoAddRepository()
+
+    private val qsSettingsRestoredRepository = FakeQSSettingsRestoredRepository()
+
+    private lateinit var underTest: RestoreReconciliationInteractor
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            RestoreReconciliationInteractor(
+                tileSpecRepository,
+                autoAddRepository,
+                qsSettingsRestoredRepository,
+                testScope.backgroundScope,
+                testDispatcher
+            )
+        underTest.start()
+    }
+
+    @Test
+    fun reconciliationInCorrectOrder_hascurrentAutoAdded() =
+        testScope.runTest {
+            val user = 10
+            val tiles by collectLastValue(tileSpecRepository.tilesSpecs(user))
+            val autoAdd by collectLastValue(autoAddRepository.autoAddedTiles(user))
+
+            // Tile b was just auto-added, so we should re-add it in position 1
+            // Tile e was auto-added before, but the user had removed it (not in the restored set).
+            // It should not be re-added
+            val specsBeforeRestore = "a,b,c,d,e"
+            val restoredSpecs = "a,c,d,f"
+            val autoAddedBeforeRestore = "b,d"
+            val restoredAutoAdded = "d,e"
+
+            val restoreData =
+                RestoreData(
+                    restoredSpecs.toTilesList(),
+                    restoredAutoAdded.toTilesSet(),
+                    user,
+                )
+
+            autoAddedBeforeRestore.toTilesSet().forEach {
+                autoAddRepository.markTileAdded(user, it)
+            }
+            tileSpecRepository.setTiles(user, specsBeforeRestore.toTilesList())
+
+            qsSettingsRestoredRepository.onDataRestored(restoreData)
+            runCurrent()
+
+            val expectedTiles = "a,b,c,d,f"
+            assertThat(tiles).isEqualTo(expectedTiles.toTilesList())
+
+            val expectedAutoAdd = "b,d,e"
+            assertThat(autoAdd).isEqualTo(expectedAutoAdd.toTilesSet())
+        }
+
+    companion object {
+        private fun String.toTilesList() = TilesSettingConverter.toTilesList(this)
+        private fun String.toTilesSet() = TilesSettingConverter.toTilesSet(this)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index bce4c06..3bf59ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -172,6 +172,9 @@
     @Test
     fun testNotAvailableControls() {
         featureEnabled = false
+
+        // Destroy previous tile
+        tile.destroy()
         tile = createTile()
 
         assertThat(tile.isAvailable).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index a0c1073..954d30ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -226,6 +226,10 @@
         assertTrue(supportedTileOnlySystemUser.isAvailable());
         when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
         assertFalse(supportedTileOnlySystemUser.isAvailable());
+
+        destroyTile(unsupportedTile);
+        destroyTile(supportedTileAllUsers);
+        destroyTile(supportedTileOnlySystemUser);
     }
 
     @Test
@@ -250,6 +254,8 @@
         mTestableLooper.processAllMessages();
         assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
                 dockedTile.getState().icon);
+
+        destroyTile(dockedTile);
     }
 
     private void setScreensaverEnabled(boolean enabled) {
@@ -257,6 +263,11 @@
                 DEFAULT_USER);
     }
 
+    private void destroyTile(QSTileImpl<?> tile) {
+        tile.destroy();
+        mTestableLooper.processAllMessages();
+    }
+
     private DreamTile constructTileForTest(boolean dreamSupported,
             boolean dreamOnlyEnabledForSystemUser) {
         return new DreamTile(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index df6993d..440270b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -216,7 +216,7 @@
     public void testSecondaryString_rotationResolverDisabled_isEmpty() {
         mTestableResources.addOverride(com.android.internal.R.bool.config_allowRotationResolver,
                 false);
-        mLockTile = new RotationLockTile(
+        RotationLockTile otherTile = new RotationLockTile(
                 mHost,
                 mUiEventLogger,
                 mTestableLooper.getLooper(),
@@ -232,10 +232,12 @@
                 new FakeSettings()
         );
 
-        mLockTile.refreshState();
+        otherTile.refreshState();
         mTestableLooper.processAllMessages();
 
-        assertEquals("", mLockTile.getState().secondaryLabel.toString());
+        assertEquals("", otherTile.getState().secondaryLabel.toString());
+
+        destroyTile(otherTile);
     }
 
     @Test
@@ -258,6 +260,12 @@
         assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on));
     }
 
+
+    private void destroyTile(QSTileImpl<?> tile) {
+        tile.destroy();
+        mTestableLooper.processAllMessages();
+    }
+
     private void enableAutoRotation() {
         when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index a2aed98..59b5953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -41,7 +41,7 @@
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget;
+import com.android.systemui.media.MediaProjectionCaptureTarget;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
index 3ae1f35..d470d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
@@ -22,13 +22,11 @@
 import android.view.View
 import android.widget.Spinner
 import androidx.test.filters.SmallTest
+import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
-import com.android.systemui.mediaprojection.permission.SINGLE_APP
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -37,8 +35,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
new file mode 100644
index 0000000..091531e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotSoundControllerTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.screenshot
+
+import android.media.MediaPlayer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import java.lang.IllegalStateException
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+
+@SmallTest
+class ScreenshotSoundControllerTest : SysuiTestCase() {
+
+    private val soundProvider = mock<ScreenshotSoundProvider>()
+    private val mediaPlayer = mock<MediaPlayer>()
+    private val bgDispatcher = UnconfinedTestDispatcher()
+    private val scope = TestScope(bgDispatcher)
+    @Before
+    fun setup() {
+        whenever(soundProvider.getScreenshotSound()).thenReturn(mediaPlayer)
+    }
+
+    @Test
+    fun init_soundLoading() {
+        createController()
+        bgDispatcher.scheduler.runCurrent()
+
+        verify(soundProvider).getScreenshotSound()
+    }
+
+    @Test
+    fun init_soundLoadingException_playAndReleaseDoNotThrow() = runTest {
+        whenever(soundProvider.getScreenshotSound()).thenThrow(IllegalStateException())
+
+        val controller = createController()
+
+        controller.playCameraSound().await()
+        controller.releaseScreenshotSound().await()
+
+        verify(mediaPlayer, never()).start()
+        verify(mediaPlayer, never()).release()
+    }
+
+    @Test
+    fun playCameraSound_soundLoadingSuccessful_mediaPlayerPlays() = runTest {
+        val controller = createController()
+
+        controller.playCameraSound().await()
+
+        verify(mediaPlayer).start()
+    }
+
+    @Test
+    fun playCameraSound_soundLoadingSuccessful_mediaPlayerReleases() = runTest {
+        val controller = createController()
+
+        controller.releaseScreenshotSound().await()
+
+        verify(mediaPlayer).release()
+    }
+
+    private fun createController() =
+        ScreenshotSoundControllerImpl(soundProvider, scope, bgDispatcher)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 31c8a3d..ed731dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -118,7 +118,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeRepository;
@@ -291,7 +291,7 @@
     @Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     @Mock protected ShadeTransitionController mShadeTransitionController;
     @Mock protected QS mQs;
-    @Mock protected QSFragment mQSFragment;
+    @Mock protected QSFragmentLegacy mQSFragment;
     @Mock protected ViewGroup mQsHeader;
     @Mock protected ViewParent mViewParent;
     @Mock protected ViewTreeObserver mViewTreeObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
index f7d2497..0c3af03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQuickSettingsContainerTest.kt
@@ -22,9 +22,9 @@
 import android.widget.FrameLayout
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.QSFragment
+import com.android.systemui.qs.QSFragmentLegacy
+import com.android.systemui.res.R
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -40,7 +40,7 @@
     @Mock private lateinit var qsFrame: View
     @Mock private lateinit var stackScroller: View
     @Mock private lateinit var keyguardStatusBar: View
-    @Mock private lateinit var qsFragment: QSFragment
+    @Mock private lateinit var qsFragment: QSFragmentLegacy
 
     private lateinit var qsView: ViewGroup
     private lateinit var qsContainer: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index fb0d4db..8138b32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -34,7 +34,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.res.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository;
 import com.android.systemui.dump.DumpManager;
@@ -46,7 +45,8 @@
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
+import com.android.systemui.qs.QSFragmentLegacy;
+import com.android.systemui.res.R;
 import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
 import com.android.systemui.screenrecord.RecordingController;
@@ -75,18 +75,17 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.user.domain.interactor.UserInteractor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
-import dagger.Lazy;
-
 import org.junit.After;
 import org.junit.Before;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import dagger.Lazy;
 import kotlinx.coroutines.test.TestScope;
 
 public class QuickSettingsControllerBaseTest extends SysuiTestCase {
@@ -109,7 +108,7 @@
     @Mock protected KeyguardBottomAreaView mQsFrame;
     @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
     @Mock protected QS mQs;
-    @Mock protected QSFragment mQSFragment;
+    @Mock protected QSFragmentLegacy mQSFragment;
     @Mock protected Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
     @Mock protected NotificationPanelViewController mNotificationPanelViewController;
     @Mock protected NotificationPanelView mPanelView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index d018cbb..2be1c09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -25,7 +25,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
@@ -35,6 +34,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
 import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -54,6 +54,7 @@
 import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -153,6 +154,7 @@
                 refreshUsersScheduler = refreshUsersScheduler,
                 guestUserInteractor = guestInteractor,
                 uiEventLogger = uiEventLogger,
+                userRestrictionChecker = mock(),
             )
         underTest =
             ShadeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index f05436f..50ce265 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -71,6 +71,7 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -117,6 +118,8 @@
     PendingIntent mPendingIntent;
     @Mock
     UserTracker mUserTracker;
+    @Mock
+    DeviceProvisionedController mDeviceProvisionedController;
 
     private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
 
@@ -141,7 +144,8 @@
                         mFlags,
                         mKeyguardNotificationVisibilityProvider,
                         mUiEventLoggerFake,
-                        mUserTracker);
+                        mUserTracker,
+                        mDeviceProvisionedController);
         mNotifInterruptionStateProvider.mUseHeadsUp = true;
     }
 
@@ -694,6 +698,25 @@
     }
 
     @Test
+    public void testShouldFullscreen_suppressedInterruptionsWhenNotProvisioned() {
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        when(mStatusBarStateController.isDreaming()).thenReturn(false);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
+        mNotifInterruptionStateProvider.addSuppressor(mSuppressInterruptions);
+
+        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+                .isEqualTo(FullScreenIntentDecision.FSI_NOT_PROVISIONED);
+        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+                .isTrue();
+        verify(mLogger, never()).logNoFullscreen(any(), any());
+        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+        verify(mLogger).logFullscreen(entry, "FSI_NOT_PROVISIONED");
+    }
+
+    @Test
     public void testShouldNotFullScreen_willHun() throws RemoteException {
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d0256..7dcbd80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -73,14 +73,14 @@
         controller = ChannelEditorDialogController(mContext, mockNoMan, dialogBuilder)
 
         channel1 = NotificationChannel(TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
-        channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_DEFAULT)
+        channel2 = NotificationChannel(TEST_CHANNEL2, TEST_CHANNEL_NAME2, IMPORTANCE_NONE)
         channelDefault = NotificationChannel(
                 NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT)
 
         group = NotificationChannelGroup(TEST_GROUP_ID, TEST_GROUP_NAME)
 
-        `when`(mockNoMan.getNotificationChannelGroupsForPackage(
-                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean()))
+        `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
                 .thenReturn(ParceledListSlice(listOf(group)))
 
         `when`(mockNoMan.areNotificationsEnabledForPackage(eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
@@ -89,11 +89,13 @@
 
     @Test
     fun testPrepareDialogForApp_noExtraChannels() {
+        channel1.group = group.id
+        channel2.group = group.id
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, clickListener)
+                channel1, appIcon, clickListener)
 
-        assertEquals(2, controller.paddedChannels.size)
+        assertEquals(2, controller.channelList.size)
     }
 
     @Test
@@ -101,39 +103,41 @@
         group.addChannel(channelDefault)
 
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channelDefault), appIcon, clickListener)
+                channelDefault, appIcon, clickListener)
 
         assertEquals("No channels should be shown when there is only the miscellaneous channel",
-                0, controller.paddedChannels.size)
+                0, controller.channelList.size)
     }
 
     @Test
-    fun testPrepareDialogForApp_noProvidedChannels_noException() {
-        group.channels = listOf()
-
-        controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(), appIcon, clickListener)
-    }
-
-    @Test
-    fun testPrepareDialogForApp_retrievesUpTo4Channels() {
+    fun testPrepareDialogForApp_AddsAllChannelsAllGroups() {
+        val group2 = NotificationChannelGroup("two", "group two")
         val channel3 = NotificationChannel("test_channel_3", "Test channel 3", IMPORTANCE_DEFAULT)
+        channel3.group = group2.id
         val channel4 = NotificationChannel("test_channel_4", "Test channel 4", IMPORTANCE_DEFAULT)
+        channel4.group = group.id
+        val channel5 = NotificationChannel("test_channel_5", "Test channel 5", IMPORTANCE_DEFAULT)
+        channel5.group = group.id
 
-        group.channels = listOf(channel1, channel2, channel3, channel4)
+        `when`(mockNoMan.getRecentBlockedNotificationChannelGroupsForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
+                .thenReturn(ParceledListSlice(listOf(group, group2)))
+
+        group.channels = listOf(channel1, channel2, channel4, channel5)
+        group2.channels = listOf(channel3)
 
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1), appIcon, clickListener)
+                channel1, appIcon, clickListener)
 
-        assertEquals("ChannelEditorDialog should fetch enough channels to show 4",
-                4, controller.paddedChannels.size)
+        assertEquals("ChannelEditorDialog should show all channels",
+                5, controller.channelList.size)
     }
 
     @Test
     fun testApply_demoteChannel() {
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, clickListener)
+                channel1, appIcon, clickListener)
 
         // propose an adjustment of channel1
         controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -145,14 +149,33 @@
 
         // Channel 2 shouldn't have changed
         assertEquals("Proposed edits should take effect after apply",
+                IMPORTANCE_NONE, channel2.importance)
+    }
+
+    @Test
+    fun testApply_promoteChannel() {
+        group.channels = listOf(channel1, channel2)
+        controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
+                channel1, appIcon, clickListener)
+
+        // propose an adjustment of channel1
+        controller.proposeEditForChannel(channel2, IMPORTANCE_DEFAULT)
+
+        controller.apply()
+
+        assertEquals("Proposed edits should take effect after apply",
                 IMPORTANCE_DEFAULT, channel2.importance)
+
+        // Channel 1 shouldn't have changed
+        assertEquals("Proposed edits should take effect after apply",
+                IMPORTANCE_DEFAULT, channel1.importance)
     }
 
     @Test
     fun testApply_demoteApp() {
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, clickListener)
+                channel1, appIcon, clickListener)
 
         controller.proposeSetAppNotificationsEnabled(false)
         controller.apply()
@@ -168,7 +191,7 @@
                 .thenReturn(false)
 
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, clickListener)
+                channel1, appIcon, clickListener)
         controller.proposeSetAppNotificationsEnabled(true)
         controller.apply()
 
@@ -181,7 +204,7 @@
         // GIVEN editor dialog
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, null)
+                channel1, appIcon, null)
 
         // WHEN user taps settings
         // Pass in any old view, it should never actually be used
@@ -197,7 +220,7 @@
 
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, null)
+                channel1, appIcon, null)
 
         // WHEN the user proposes a change
         controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -214,7 +237,7 @@
 
         group.channels = listOf(channel1, channel2)
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
-                setOf(channel1, channel2), appIcon, null)
+                channel1, appIcon, null)
 
         // WHEN the user proposes a change
         controller.proposeEditForChannel(channel1, IMPORTANCE_NONE)
@@ -236,7 +259,6 @@
         const val TEST_PACKAGE_NAME = "test_package"
         const val TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME
         const val TEST_UID = 1
-        const val MULTIPLE_CHANNEL_COUNT = 2
         const val TEST_CHANNEL = "test_channel"
         const val TEST_CHANNEL_NAME = "Test Channel Name"
         const val TEST_CHANNEL2 = "test_channel2"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index ac8b42a..e373143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -481,33 +481,6 @@
     }
 
     @Test
-    public void testGetNumUniqueChildren_defaultChannel() throws Exception {
-        ExpandableNotificationRow groupRow = mNotificationTestHelper.createGroup();
-
-        assertEquals(1, groupRow.getNumUniqueChannels());
-    }
-
-    @Test
-    public void testGetNumUniqueChildren_multiChannel() throws Exception {
-        ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
-
-        List<ExpandableNotificationRow> childRows =
-                group.getChildrenContainer().getAttachedChildren();
-        // Give each child a unique channel id/name.
-        int i = 0;
-        for (ExpandableNotificationRow childRow : childRows) {
-            modifyRanking(childRow.getEntry())
-                    .setChannel(
-                            new NotificationChannel(
-                                    "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
-                    .build();
-            i++;
-        }
-
-        assertEquals(3, group.getNumUniqueChannels());
-    }
-
-    @Test
     public void testIconScrollXAfterTranslationAndReset() throws Exception {
         ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
 
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 9e0f83c..0f1e63f 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
@@ -462,7 +462,6 @@
                 eq(mChannelEditorDialogController),
                 eq(statusBarNotification.getPackageName()),
                 any(NotificationChannel.class),
-                anySet(),
                 eq(entry),
                 any(NotificationInfo.OnSettingsClickListener.class),
                 any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -496,7 +495,6 @@
                 eq(mChannelEditorDialogController),
                 eq(statusBarNotification.getPackageName()),
                 any(NotificationChannel.class),
-                anySet(),
                 eq(entry),
                 any(NotificationInfo.OnSettingsClickListener.class),
                 any(NotificationInfo.OnAppSettingsClickListener.class),
@@ -528,7 +526,6 @@
                 eq(mChannelEditorDialogController),
                 eq(statusBarNotification.getPackageName()),
                 any(NotificationChannel.class),
-                anySet(),
                 eq(entry),
                 any(NotificationInfo.OnSettingsClickListener.class),
                 any(NotificationInfo.OnAppSettingsClickListener.class),
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 f0b4dd4..b59385c 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
@@ -92,7 +92,6 @@
     private static final String TEST_PACKAGE_NAME = "test_package";
     private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
     private static final int TEST_UID = 1;
-    private static final int MULTIPLE_CHANNEL_COUNT = 2;
     private static final String TEST_CHANNEL = "test_channel";
     private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
 
@@ -100,8 +99,6 @@
     private NotificationInfo mNotificationInfo;
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
-    private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
-    private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
     private StatusBarNotification mSbn;
     private NotificationEntry mEntry;
     private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
@@ -162,11 +159,9 @@
         // Some test channels.
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
-        mNotificationChannelSet.add(mNotificationChannel);
         mDefaultNotificationChannel = new NotificationChannel(
                 NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
                 IMPORTANCE_LOW);
-        mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0);
         mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
@@ -185,7 +180,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -212,7 +206,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -235,7 +228,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -267,7 +259,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 entry,
                 null,
                 null,
@@ -291,7 +282,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -320,7 +310,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -344,7 +333,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -367,7 +355,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mDefaultNotificationChannel,
-                mDefaultNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -394,7 +381,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mDefaultNotificationChannel,
-                mDefaultNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -417,7 +403,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -441,7 +426,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
@@ -470,7 +454,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -494,7 +477,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
@@ -519,7 +501,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -536,7 +517,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> { },
                 null,
@@ -551,86 +531,6 @@
     }
 
     @Test
-    public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mOnUserInteractionCallback,
-                mChannelEditorDialogController,
-                TEST_PACKAGE_NAME, mNotificationChannel,
-                createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
-                mEntry,
-                (View v, NotificationChannel c, int appUid) -> {
-                    assertEquals(null, c);
-                    latch.countDown();
-                },
-                null,
-                mUiEventLogger,
-                true,
-                true,
-                true,
-                mAssistantFeedbackController,
-                mMetricsLogger);
-
-        mNotificationInfo.findViewById(R.id.info).performClick();
-        // Verify that listener was triggered.
-        assertEquals(0, latch.getCount());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
-            throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mOnUserInteractionCallback,
-                mChannelEditorDialogController,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
-                mEntry,
-                null,
-                null,
-                mUiEventLogger,
-                true,
-                false,
-                true,
-                mAssistantFeedbackController,
-                mMetricsLogger);
-        final TextView channelNameView =
-                mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(GONE, channelNameView.getVisibility());
-    }
-
-    @Test
-    @UiThreadTest
-    public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
-        mNotificationInfo.bindNotification(
-                mMockPackageManager,
-                mMockINotificationManager,
-                mOnUserInteractionCallback,
-                mChannelEditorDialogController,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
-                mEntry,
-                null,
-                null,
-                mUiEventLogger,
-                true,
-                false,
-                true,
-                mAssistantFeedbackController,
-                mMetricsLogger);
-        assertEquals(GONE, mNotificationInfo.findViewById(
-                R.id.interruptiveness_settings).getVisibility());
-        assertEquals(VISIBLE, mNotificationInfo.findViewById(
-                R.id.non_configurable_multichannel_text).getVisibility());
-    }
-
-    @Test
     public void testBindNotification_whenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(
                 mMockPackageManager,
@@ -639,7 +539,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -683,7 +582,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -727,7 +625,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -755,7 +652,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -778,7 +674,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -803,7 +698,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -825,7 +719,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -847,7 +740,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -869,7 +761,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -893,7 +784,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -918,7 +808,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -946,7 +835,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -974,7 +862,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1003,7 +890,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1031,7 +917,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1067,7 +952,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1096,7 +980,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1138,7 +1021,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1176,7 +1058,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1209,7 +1090,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1246,7 +1126,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1285,7 +1164,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1317,7 +1195,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1356,7 +1233,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1386,7 +1262,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1418,7 +1293,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1454,7 +1328,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1488,7 +1361,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1522,7 +1394,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1549,7 +1420,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 null,
@@ -1562,21 +1432,4 @@
 
         assertFalse(mNotificationInfo.willBeRemoved());
     }
-
-    private Set<NotificationChannel> createMultipleChannelSet(int howMany) {
-        Set<NotificationChannel> multiChannelSet = new HashSet<>();
-        for (int i = 0; i < howMany; i++) {
-            if (i == 0) {
-                multiChannelSet.add(mNotificationChannel);
-                continue;
-            }
-
-            NotificationChannel channel = new NotificationChannel(
-                    TEST_CHANNEL, TEST_CHANNEL_NAME + i, IMPORTANCE_LOW);
-
-            multiChannelSet.add(channel);
-        }
-
-        return multiChannelSet;
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index e42ce27..ccedd36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -83,8 +83,6 @@
     private PartialConversationInfo mInfo;
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
-    private Set<NotificationChannel> mNotificationChannelSet = new HashSet<>();
-    private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
     private StatusBarNotification mSbn;
     private NotificationEntry mEntry;
 
@@ -144,11 +142,9 @@
         // Some test channels.
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
-        mNotificationChannelSet.add(mNotificationChannel);
         mDefaultNotificationChannel = new NotificationChannel(
                 NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
                 IMPORTANCE_LOW);
-        mDefaultNotificationChannelSet.add(mDefaultNotificationChannel);
         Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
                 .setContentTitle(new SpannableString("title"))
                 .build();
@@ -166,7 +162,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 true,
@@ -185,7 +180,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 true,
@@ -202,7 +196,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 true,
@@ -228,7 +221,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 entry,
                 null,
                 true,
@@ -247,7 +239,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
@@ -271,7 +262,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
@@ -294,7 +284,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 true,
@@ -311,7 +300,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
@@ -330,7 +318,6 @@
                 mChannelEditorDialogController,
                 TEST_PACKAGE_NAME,
                 mNotificationChannel,
-                mNotificationChannelSet,
                 mEntry,
                 null,
                 true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 5c3dde5..0b171b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -364,7 +364,8 @@
                         mock(NotifPipelineFlags.class),
                         mock(KeyguardNotificationVisibilityProvider.class),
                         mock(UiEventLogger.class),
-                        mUserTracker);
+                        mUserTracker,
+                        mDeviceProvisionedController);
 
         mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
         mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
@@ -1169,7 +1170,8 @@
                 NotifPipelineFlags flags,
                 KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
                 UiEventLogger uiEventLogger,
-                UserTracker userTracker) {
+                UserTracker userTracker,
+                DeviceProvisionedController deviceProvisionedController) {
             super(
                     contentResolver,
                     powerManager,
@@ -1183,7 +1185,8 @@
                     flags,
                     keyguardNotificationVisibilityProvider,
                     uiEventLogger,
-                    userTracker
+                    userTracker,
+                    deviceProvisionedController
             );
             mUseHeadsUp = true;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 99e4030..b54fbd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -90,6 +90,8 @@
     private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
     override val defaultMobileIconGroup = _defaultMobileIconGroup
 
+    override val isAnySimSecure = MutableStateFlow(false)
+
     fun setSubscriptions(subs: List<SubscriptionModel>) {
         _subscriptions.value = subs
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index d005972..4d4f33b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -135,6 +135,7 @@
                 FakeAirplaneModeRepository(),
                 wifiRepository,
                 mock(),
+                mock(),
             )
 
         demoRepo =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 6f9764a..9148c75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,6 +37,8 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.internal.telephony.PhoneConstants
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.R
 import com.android.settingslib.mobile.MobileMappings
 import com.android.systemui.SysuiTestCase
@@ -104,6 +106,7 @@
     @Mock private lateinit var logger: MobileInputLogger
     @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
+    @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
 
     private val mobileMappings = FakeMobileMappingsProxy()
     private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
@@ -214,6 +217,7 @@
                 airplaneModeRepository,
                 wifiRepository,
                 fullConnectionFactory,
+                updateMonitor,
             )
 
         testScope.runCurrent()
@@ -1048,6 +1052,7 @@
                     airplaneModeRepository,
                     wifiRepository,
                     fullConnectionFactory,
+                    updateMonitor
                 )
 
             val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1103,7 +1108,6 @@
     @Test
     fun carrierConfig_initialValueIsFetched() =
         testScope.runTest {
-
             // Value starts out false
             assertThat(underTest.defaultDataSubRatConfig.value.showAtLeast3G).isFalse()
 
@@ -1151,6 +1155,26 @@
             assertThat(latest).isEqualTo(null)
         }
 
+    @Test
+    fun anySimSecure_propagatesStateFromKeyguardUpdateMonitor() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isAnySimSecure)
+            assertThat(latest).isFalse()
+
+            val updateMonitorCallback = argumentCaptor<KeyguardUpdateMonitorCallback>()
+            verify(updateMonitor).registerCallback(updateMonitorCallback.capture())
+
+            whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+            updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+            assertThat(latest).isTrue()
+
+            whenever(updateMonitor.isSimPinSecure).thenReturn(false)
+            updateMonitorCallback.value.onSimStateChanged(0, 0, 0)
+
+            assertThat(latest).isFalse()
+        }
+
     private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
         runCurrent()
         val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index bbc49c8..af941d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -34,7 +34,6 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.GuestResetOrExitSessionReceiver
 import com.android.systemui.GuestResumeSessionReceiver
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Text
@@ -45,6 +44,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
 import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
@@ -1120,6 +1120,7 @@
                     ),
                 uiEventLogger = uiEventLogger,
                 featureFlags = featureFlags,
+                userRestrictionChecker = mock(),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 2433e12..a8db368 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -267,6 +267,7 @@
                     refreshUsersScheduler = refreshUsersScheduler,
                     guestUserInteractor = guestUserInteractor,
                     uiEventLogger = uiEventLogger,
+                    userRestrictionChecker = mock(),
                 )
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 8c88f95..6932f5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -45,6 +45,7 @@
 import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
 import com.android.systemui.user.shared.model.UserActionModel
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -176,6 +177,7 @@
                         refreshUsersScheduler = refreshUsersScheduler,
                         guestUserInteractor = guestUserInteractor,
                         uiEventLogger = uiEventLogger,
+                        userRestrictionChecker = mock(),
                     ),
                 guestUserInteractor = guestUserInteractor,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 94ed608..e59e475 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -15,104 +15,119 @@
  */
 package com.android.systemui.wmshell
 
-import android.content.ContentResolver
 import android.content.Context
+import android.content.Intent
 import android.content.SharedPreferences
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.core.content.edit
 import androidx.test.filters.SmallTest
 import com.android.systemui.model.SysUiStateTest
 import com.android.wm.shell.bubbles.Bubble
 import com.android.wm.shell.bubbles.BubbleEducationController
 import com.android.wm.shell.bubbles.PREF_MANAGED_EDUCATION
 import com.android.wm.shell.bubbles.PREF_STACK_EDUCATION
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mockito
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BubbleEducationControllerTest : SysUiStateTest() {
-    private val sharedPrefsEditor = Mockito.mock(SharedPreferences.Editor::class.java)
-    private val sharedPrefs = Mockito.mock(SharedPreferences::class.java)
-    private val context = Mockito.mock(Context::class.java)
+
+    private lateinit var sharedPrefs: SharedPreferences
     private lateinit var sut: BubbleEducationController
 
     @Before
     fun setUp() {
-        Mockito.`when`(context.packageName).thenReturn("packageName")
-        Mockito.`when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs)
-        Mockito.`when`(context.contentResolver)
-            .thenReturn(Mockito.mock(ContentResolver::class.java))
-        Mockito.`when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
-        sut = BubbleEducationController(context)
+        sharedPrefs = mContext.getSharedPreferences(mContext.packageName, Context.MODE_PRIVATE)
+        sharedPrefs.edit {
+            remove(PREF_STACK_EDUCATION)
+            remove(PREF_MANAGED_EDUCATION)
+        }
+        sut = BubbleEducationController(mContext)
     }
 
     @Test
     fun testSeenStackEducation_read() {
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+        sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
         assertEquals(sut.hasSeenStackEducation, true)
-        Mockito.verify(sharedPrefs).getBoolean(PREF_STACK_EDUCATION, false)
     }
 
     @Test
     fun testSeenStackEducation_write() {
         sut.hasSeenStackEducation = true
-        Mockito.verify(sharedPrefsEditor).putBoolean(PREF_STACK_EDUCATION, true)
+        assertThat(sharedPrefs.getBoolean(PREF_STACK_EDUCATION, false)).isTrue()
     }
 
     @Test
     fun testSeenManageEducation_read() {
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+        sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
         assertEquals(sut.hasSeenManageEducation, true)
-        Mockito.verify(sharedPrefs).getBoolean(PREF_MANAGED_EDUCATION, false)
     }
 
     @Test
     fun testSeenManageEducation_write() {
         sut.hasSeenManageEducation = true
-        Mockito.verify(sharedPrefsEditor).putBoolean(PREF_MANAGED_EDUCATION, true)
+        assertThat(sharedPrefs.getBoolean(PREF_MANAGED_EDUCATION, false)).isTrue()
     }
 
     @Test
     fun testShouldShowStackEducation() {
-        val bubble = Mockito.mock(Bubble::class.java)
         // When bubble is null
         assertEquals(sut.shouldShowStackEducation(null), false)
+        var bubble = createFakeBubble(isConversational = false)
         // When bubble is not conversation
-        Mockito.`when`(bubble.isConversation).thenReturn(false)
         assertEquals(sut.shouldShowStackEducation(bubble), false)
         // When bubble is conversation and has seen stack edu
-        Mockito.`when`(bubble.isConversation).thenReturn(true)
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+        bubble = createFakeBubble(isConversational = true)
+        sharedPrefs.edit { putBoolean(PREF_STACK_EDUCATION, true) }
         assertEquals(sut.shouldShowStackEducation(bubble), false)
         // When bubble is conversation and has not seen stack edu
-        Mockito.`when`(bubble.isConversation).thenReturn(true)
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+        sharedPrefs.edit { remove(PREF_STACK_EDUCATION) }
         assertEquals(sut.shouldShowStackEducation(bubble), true)
     }
 
     @Test
     fun testShouldShowManageEducation() {
-        val bubble = Mockito.mock(Bubble::class.java)
         // When bubble is null
         assertEquals(sut.shouldShowManageEducation(null), false)
+        var bubble = createFakeBubble(isConversational = false)
         // When bubble is not conversation
-        Mockito.`when`(bubble.isConversation).thenReturn(false)
         assertEquals(sut.shouldShowManageEducation(bubble), false)
         // When bubble is conversation and has seen stack edu
-        Mockito.`when`(bubble.isConversation).thenReturn(true)
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true)
+        bubble = createFakeBubble(isConversational = true)
+        sharedPrefs.edit { putBoolean(PREF_MANAGED_EDUCATION, true) }
         assertEquals(sut.shouldShowManageEducation(bubble), false)
         // When bubble is conversation and has not seen stack edu
-        Mockito.`when`(bubble.isConversation).thenReturn(true)
-        Mockito.`when`(sharedPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(false)
+        sharedPrefs.edit { remove(PREF_MANAGED_EDUCATION) }
         assertEquals(sut.shouldShowManageEducation(bubble), true)
     }
+
+    private fun createFakeBubble(isConversational: Boolean): Bubble {
+        return if (isConversational) {
+            val shortcutInfo = ShortcutInfo.Builder(mContext, "fakeId").build()
+            Bubble(
+                "key",
+                shortcutInfo,
+                /* desiredHeight= */ 6,
+                Resources.ID_NULL,
+                "title",
+                /* taskId= */ 0,
+                "locus",
+                /* isDismissable= */ true,
+                directExecutor()
+            ) {}
+        } else {
+            val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName)
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index d8511e8..65b8b55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -126,6 +126,7 @@
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -391,7 +392,8 @@
                         mock(NotifPipelineFlags.class),
                         mock(KeyguardNotificationVisibilityProvider.class),
                         mock(UiEventLogger.class),
-                        mock(UserTracker.class)
+                        mock(UserTracker.class),
+                        mock(DeviceProvisionedController.class)
                 );
 
         mShellTaskOrganizer = new ShellTaskOrganizer(mock(ShellInit.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
index 4e14bbf6..0df235d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableNotificationInterruptStateProviderImpl.java
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -48,7 +49,8 @@
             NotifPipelineFlags flags,
             KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider,
             UiEventLogger uiEventLogger,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            DeviceProvisionedController deviceProvisionedController) {
         super(contentResolver,
                 powerManager,
                 ambientDisplayConfiguration,
@@ -61,7 +63,8 @@
                 flags,
                 keyguardNotificationVisibilityProvider,
                 uiEventLogger,
-                userTracker);
+                userTracker,
+                deviceProvisionedController);
         mUseHeadsUp = true;
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 21a5eb7..28557d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.broadcast
 
 import android.content.BroadcastReceiver
+import android.content.BroadcastReceiver.PendingResult
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
@@ -28,7 +29,6 @@
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.settings.UserTracker
-import java.lang.IllegalStateException
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.Executor
 
@@ -96,8 +96,14 @@
     /**
      * Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter]
      * that matches the intent.
+     *
+     * A non-null [pendingResult] can be used to pass the sending user.
      */
-    fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) {
+    fun sendIntentToMatchingReceiversOnly(
+        context: Context,
+        intent: Intent,
+        pendingResult: PendingResult? = null
+    ) {
         receivers.forEach {
             if (
                 it.filter.match(
@@ -107,6 +113,9 @@
                     /* logTag= */ "FakeBroadcastDispatcher",
                 ) > 0
             ) {
+                if (pendingResult != null) {
+                    it.receiver.pendingResult = pendingResult
+                }
                 it.receiver.onReceive(context, intent)
             }
         }
@@ -130,4 +139,19 @@
         val receiver: BroadcastReceiver,
         val filter: IntentFilter,
     )
+
+    companion object {
+        fun fakePendingResultForUser(userId: Int) =
+            PendingResult(
+                /* resultCode = */ 0,
+                /* resultData = */ "",
+                /* resultExtras = */ null,
+                /* type = */ PendingResult.TYPE_REGISTERED,
+                /* ordered = */ false,
+                /* sticky = */ false,
+                /* token = */ null,
+                userId,
+                /* flags = */ 0,
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
index ceab8e9..945aaed 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
@@ -50,6 +50,7 @@
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.utils.UserRestrictionChecker
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.test.TestScope
 import org.mockito.Mockito.mock
@@ -137,6 +138,7 @@
                 refreshUsersScheduler = mock(RefreshUsersScheduler::class.java),
                 guestUserInteractor = mock(GuestUserInteractor::class.java),
                 uiEventLogger = mock(UiEventLogger::class.java),
+                userRestrictionChecker = mock(UserRestrictionChecker::class.java),
             )
         return WithDependencies(
             trustRepository = trustRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
index 9ea079f..57ad2828 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeAutoAddRepository.kt
@@ -16,15 +16,16 @@
 
 package com.android.systemui.qs.pipeline.data.repository
 
+import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 
 class FakeAutoAddRepository : AutoAddRepository {
 
     private val autoAddedTilesPerUser = mutableMapOf<Int, MutableStateFlow<Set<TileSpec>>>()
 
-    override fun autoAddedTiles(userId: Int): Flow<Set<TileSpec>> {
+    override suspend fun autoAddedTiles(userId: Int): StateFlow<Set<TileSpec>> {
         return getFlow(userId)
     }
 
@@ -39,4 +40,8 @@
 
     private fun getFlow(userId: Int): MutableStateFlow<Set<TileSpec>> =
         autoAddedTilesPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) }
+
+    override suspend fun reconcileRestore(restoreData: RestoreData) {
+        with(getFlow(restoreData.userId)) { value = value + restoreData.restoredAutoAddedTiles }
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
new file mode 100644
index 0000000..e0c2154
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeQSSettingsRestoredRepository.kt
@@ -0,0 +1,16 @@
+package com.android.systemui.qs.pipeline.data.repository
+
+import com.android.systemui.qs.pipeline.data.model.RestoreData
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeQSSettingsRestoredRepository : QSSettingsRestoredRepository {
+    private val _restoreData = MutableSharedFlow<RestoreData>()
+
+    override val restoreData: Flow<RestoreData>
+        get() = _restoreData
+
+    suspend fun onDataRestored(restoreData: RestoreData) {
+        _restoreData.emit(restoreData)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
index aa8dbe1..ae4cf3a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeTileSpecRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.pipeline.data.repository
 
+import com.android.systemui.qs.pipeline.data.model.RestoreData
 import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Companion.POSITION_AT_END
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import kotlinx.coroutines.flow.Flow
@@ -26,7 +27,7 @@
 
     private val tilesPerUser = mutableMapOf<Int, MutableStateFlow<List<TileSpec>>>()
 
-    override fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
+    override suspend fun tilesSpecs(userId: Int): Flow<List<TileSpec>> {
         return getFlow(userId).asStateFlow()
     }
 
@@ -57,4 +58,13 @@
 
     private fun getFlow(userId: Int): MutableStateFlow<List<TileSpec>> =
         tilesPerUser.getOrPut(userId) { MutableStateFlow(emptyList()) }
+
+    override suspend fun reconcileRestore(
+        restoreData: RestoreData,
+        currentAutoAdded: Set<TileSpec>
+    ) {
+        with(getFlow(restoreData.userId)) {
+            value = UserTileSpecRepository.reconcileTiles(value, currentAutoAdded, restoreData)
+        }
+    }
 }
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 0480c22..11189cf 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -25,5 +25,12 @@
     name: "send_a11y_events_based_on_state"
     namespace: "accessibility"
     description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
-bug: "295575684"
-}
\ No newline at end of file
+    bug: "295575684"
+}
+
+flag {
+    name: "add_window_token_without_lock"
+    namespace: "accessibility"
+    description: "Calls WMS.addWindowToken without holding A11yManagerService#mLock"
+    bug: "297972548"
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 05b6eb4..fa73cff 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1506,11 +1506,17 @@
         }
     }
 
-    public void onAdded() {
+    /**
+     * Called when the connection is first created. Add a window token for all known displays.
+     * <p>
+     * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+     * lock because this calls out to WindowManagerService.
+     */
+    void addWindowTokensForAllDisplays() {
         final Display[] displays = mDisplayManager.getDisplays();
         for (int i = 0; i < displays.length; i++) {
             final int displayId = displays[i].getDisplayId();
-            onDisplayAdded(displayId);
+            addWindowTokenForDisplay(displayId);
         }
     }
 
@@ -1518,9 +1524,13 @@
      * Called whenever a logical display has been added to the system. Add a window token for adding
      * an accessibility overlay.
      *
+     * <p>
+     * <strong>Note:</strong> Should not be called while holding the AccessibilityManagerService
+     * lock because this calls out to WindowManagerService.
+     *
      * @param displayId The id of the logical display that was added.
      */
-    public void onDisplayAdded(int displayId) {
+    void addWindowTokenForDisplay(int displayId) {
         final long identity = Binder.clearCallingIdentity();
         try {
             final IBinder overlayWindowToken = new Binder();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 93ba362..60d4ee6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -154,6 +154,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IntPair;
+import com.android.internal.util.Preconditions;
 import com.android.server.AccessibilityManagerInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -4500,6 +4501,20 @@
         private int mSystemUiUid = 0;
 
         AccessibilityDisplayListener(Context context, Handler handler) {
+            if (Flags.addWindowTokenWithoutLock()) {
+                // Avoid concerns about one thread adding displays while another thread removes
+                // them by ensuring the looper is the main looper and the DisplayListener
+                // callbacks are always executed on the one main thread.
+                final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+                final String errorMessage =
+                        "AccessibilityDisplayListener must use the main handler";
+                if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                    Preconditions.checkArgument(isMainHandler, errorMessage);
+                } else if (!isMainHandler) {
+                    Slog.e(LOG_TAG, errorMessage);
+                }
+            }
+
             mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
             mDisplayManager.registerDisplayListener(this, handler);
             initializeDisplayList();
@@ -4541,11 +4556,21 @@
 
         @Override
         public void onDisplayAdded(int displayId) {
+            if (Flags.addWindowTokenWithoutLock()) {
+                final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+                final String errorMessage = "onDisplayAdded must be called from the main thread";
+                if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                    Preconditions.checkArgument(isMainThread, errorMessage);
+                } else if (!isMainThread) {
+                    Slog.e(LOG_TAG, errorMessage);
+                }
+            }
             final Display display = mDisplayManager.getDisplay(displayId);
             if (!isValidDisplay(display)) {
                 return;
             }
 
+            final List<AccessibilityServiceConnection> services;
             synchronized (mLock) {
                 mDisplaysList.add(display);
                 mA11yOverlayLayers.put(
@@ -4554,21 +4579,42 @@
                     mInputFilter.onDisplayAdded(display);
                 }
                 AccessibilityUserState userState = getCurrentUserStateLocked();
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    final List<AccessibilityServiceConnection> services = userState.mBoundServices;
-                    for (int i = 0; i < services.size(); i++) {
-                        AccessibilityServiceConnection boundClient = services.get(i);
-                        boundClient.onDisplayAdded(displayId);
+                if (Flags.addWindowTokenWithoutLock()) {
+                    services = new ArrayList<>(userState.mBoundServices);
+                } else {
+                    services = userState.mBoundServices;
+                    if (displayId != Display.DEFAULT_DISPLAY) {
+                        for (int i = 0; i < services.size(); i++) {
+                            AccessibilityServiceConnection boundClient = services.get(i);
+                            boundClient.addWindowTokenForDisplay(displayId);
+                        }
                     }
                 }
                 updateMagnificationLocked(userState);
                 updateWindowsForAccessibilityCallbackLocked(userState);
                 notifyClearAccessibilityCacheLocked();
             }
+            if (Flags.addWindowTokenWithoutLock()) {
+                if (displayId != Display.DEFAULT_DISPLAY) {
+                    for (int i = 0; i < services.size(); i++) {
+                        AccessibilityServiceConnection boundClient = services.get(i);
+                        boundClient.addWindowTokenForDisplay(displayId);
+                    }
+                }
+            }
         }
 
         @Override
         public void onDisplayRemoved(int displayId) {
+            if (Flags.addWindowTokenWithoutLock()) {
+                final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+                final String errorMessage = "onDisplayRemoved must be called from the main thread";
+                if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                    Preconditions.checkArgument(isMainThread, errorMessage);
+                } else if (!isMainThread) {
+                    Slog.e(LOG_TAG, errorMessage);
+                }
+            }
             synchronized (mLock) {
                 if (!removeDisplayFromList(displayId)) {
                     return;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 9e70073..7a2a602 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -44,7 +44,6 @@
 import android.view.Display;
 import android.view.MotionEvent;
 
-
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -169,6 +168,10 @@
 
     @Override
     public void onServiceConnected(ComponentName componentName, IBinder service) {
+        AccessibilityUserState userState = mUserStateWeakReference.get();
+        if (userState != null && Flags.addWindowTokenWithoutLock()) {
+            addWindowTokensForAllDisplays();
+        }
         synchronized (mLock) {
             if (mService != service) {
                 if (mService != null) {
@@ -184,7 +187,6 @@
                 }
             }
             mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
-            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             userState.addServiceLocked(this);
             mSystemSupport.onClientChangeLocked(false);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index ab6cc71..693526a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -224,7 +224,9 @@
 
     void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
         if (!mBoundServices.contains(serviceConnection)) {
-            serviceConnection.onAdded();
+            if (!Flags.addWindowTokenWithoutLock()) {
+                serviceConnection.addWindowTokensForAllDisplays();
+            }
             mBoundServices.add(serviceConnection);
             mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
             mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 208acdf..53c629a 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -25,9 +25,11 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
+import android.os.Looper;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -35,6 +37,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.utils.Slogf;
 import com.android.server.wm.WindowManagerInternal;
 
@@ -98,46 +101,47 @@
         accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
         Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
                 accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
-        synchronized (mLock) {
-            if (mUiAutomationService != null) {
-                throw new IllegalStateException(
-                        "UiAutomationService " + mUiAutomationService.mServiceInterface
-                                + "already registered!");
-            }
-
-            try {
-                owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
-            } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
-                        re);
-                return;
-            }
-
-            mUiAutomationFlags = flags;
-            mSystemSupport = systemSupport;
-            // Ignore registering UiAutomation if it is not allowed to use the accessibility
-            // subsystem.
-            if (!useAccessibility()) {
-                return;
-            }
-            mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
-                    mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
-                    systemActionPerformer, awm);
-            mUiAutomationServiceOwner = owner;
-            mUiAutomationService.mServiceInterface = serviceClient;
-            try {
-                mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
-                        0);
-            } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Failed registering death link: " + re);
-                destroyUiAutomationService();
-                return;
-            }
-
-            mUiAutomationService.onAdded();
-
-            mUiAutomationService.connectServiceUnknownThread();
+        if (mUiAutomationService != null) {
+            throw new IllegalStateException(
+                    "UiAutomationService " + mUiAutomationService.mServiceInterface
+                            + "already registered!");
         }
+
+        try {
+            owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!",
+                    re);
+            return;
+        }
+
+        mUiAutomationFlags = flags;
+        mSystemSupport = systemSupport;
+        // Ignore registering UiAutomation if it is not allowed to use the accessibility
+        // subsystem.
+        if (!useAccessibility()) {
+            return;
+        }
+        mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
+                mainHandler, mLock, securityPolicy, systemSupport, trace, windowManagerInternal,
+                systemActionPerformer, awm);
+        mUiAutomationServiceOwner = owner;
+        mUiAutomationService.mServiceInterface = serviceClient;
+        try {
+            mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService,
+                    0);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Failed registering death link: " + re);
+            destroyUiAutomationService();
+            return;
+        }
+
+        if (!Flags.addWindowTokenWithoutLock()) {
+            mUiAutomationService.addWindowTokensForAllDisplays();
+        }
+        // UiAutomationService#connectServiceUnknownThread posts to a handler
+        // so this call should return immediately.
+        mUiAutomationService.connectServiceUnknownThread();
     }
 
     void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) {
@@ -253,6 +257,13 @@
             super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock,
                     securityPolicy, systemSupport, trace, windowManagerInternal,
                     systemActionPerformer, awm);
+            final boolean isMainHandler = mainHandler.getLooper() == Looper.getMainLooper();
+            final String errorMessage = "UiAutomationService must use the main handler";
+            if (Build.IS_USERDEBUG || Build.IS_ENG) {
+                Preconditions.checkArgument(isMainHandler, errorMessage);
+            } else if (!isMainHandler) {
+                Slog.e(LOG_TAG, errorMessage);
+            }
             mMainHandler = mainHandler;
             setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
         }
@@ -274,6 +285,9 @@
                     // If the serviceInterface is null, the UiAutomation has been shut down on
                     // another thread.
                     if (serviceInterface != null) {
+                        if (Flags.addWindowTokenWithoutLock()) {
+                            mUiAutomationService.addWindowTokensForAllDisplays();
+                        }
                         if (mTrace.isA11yTracingEnabledForTypes(
                                 AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
                             mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
@@ -286,7 +300,7 @@
                                 mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY));
                     }
                 } catch (RemoteException re) {
-                    Slog.w(LOG_TAG, "Error initialized connection", re);
+                    Slog.w(LOG_TAG, "Error initializing connection", re);
                     destroyUiAutomationService();
                 }
             });
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index ba45339..c791498 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -155,6 +155,7 @@
             "debug.cdm.cdmservice.removal_time_window";
 
     private static final long ASSOCIATION_REMOVAL_TIME_WINDOW_DEFAULT = DAYS.toMillis(90);
+    private static final int MAX_CN_LENGTH = 500;
 
     private final ActivityManager mActivityManager;
     private final OnPackageVisibilityChangeListener mOnPackageVisibilityChangeListener;
@@ -757,6 +758,9 @@
             String callingPackage = component.getPackageName();
             checkCanCallNotificationApi(callingPackage);
             // TODO: check userId.
+            if (component.flattenToString().length() > MAX_CN_LENGTH) {
+                throw new IllegalArgumentException("Component name is too long.");
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 return PendingIntent.getActivityAsUser(getContext(),
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index e8839a2..720687e 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -562,7 +562,8 @@
     private byte[] constructToken(D2DHandshakeContext.Role role, byte[] authValue)
             throws GeneralSecurityException {
         MessageDigest hash = MessageDigest.getInstance("SHA-256");
-        byte[] roleUtf8 = role.name().getBytes(StandardCharsets.UTF_8);
+        String roleName = role == Role.INITIATOR ? "Initiator" : "Responder";
+        byte[] roleUtf8 = roleName.getBytes(StandardCharsets.UTF_8);
         int tokenLength = roleUtf8.length + authValue.length;
         return hash.digest(ByteBuffer.allocate(tokenLength)
                 .put(roleUtf8)
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d9c2694..6521fab 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -194,6 +194,7 @@
         "notification_flags_lib",
         "camera_platform_flags_core_java_lib",
         "biometrics_flags_lib",
+        "am_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 25ca509c..8cc2665 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -265,17 +265,17 @@
 
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
-            mStorageManagerService.onUnlockUser(user.getUserIdentifier());
+            mStorageManagerService.onUserUnlocking(user.getUserIdentifier());
         }
 
         @Override
         public void onUserStopped(@NonNull TargetUser user) {
-            mStorageManagerService.onCleanupUser(user.getUserIdentifier());
+            mStorageManagerService.onUserStopped(user.getUserIdentifier());
         }
 
         @Override
         public void onUserStopping(@NonNull TargetUser user) {
-            mStorageManagerService.onStopUser(user.getUserIdentifier());
+            mStorageManagerService.onUserStopping(user.getUserIdentifier());
         }
 
         @Override
@@ -1163,8 +1163,8 @@
         }
     }
 
-    private void onUnlockUser(int userId) {
-        Slog.d(TAG, "onUnlockUser " + userId);
+    private void onUserUnlocking(int userId) {
+        Slog.d(TAG, "onUserUnlocking " + userId);
 
         if (userId != UserHandle.USER_SYSTEM) {
             // Check if this user shares media with another user
@@ -1227,8 +1227,8 @@
         }
     }
 
-    private void onCleanupUser(int userId) {
-        Slog.d(TAG, "onCleanupUser " + userId);
+    private void onUserStopped(int userId) {
+        Slog.d(TAG, "onUserStopped " + userId);
 
         try {
             mVold.onUserStopped(userId);
@@ -1242,8 +1242,8 @@
         }
     }
 
-    private void onStopUser(int userId) {
-        Slog.i(TAG, "onStopUser " + userId);
+    private void onUserStopping(int userId) {
+        Slog.i(TAG, "onUserStopping " + userId);
         try {
             mStorageSessionController.onUserStopping(userId);
         } catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index c20f0aa..9716cf6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -225,7 +225,7 @@
     /**
      * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
      */
-    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
 
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
new file mode 100644
index 0000000..af1200e
--- /dev/null
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -0,0 +1,10 @@
+aconfig_declarations {
+    name: "am_flags",
+    package: "com.android.server.am",
+    srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "am_flags_lib",
+    aconfig_declarations: "am_flags",
+}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 128bbdf..907069d 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -82,6 +82,7 @@
 import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
 import com.android.server.pm.UserManagerInternal;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
 import java.util.Arrays;
@@ -571,8 +572,18 @@
             builder = new BatteryUsageStatsQuery.Builder()
                     .includeProcessStateData()
                     .aggregateSnapshots(lastUidBatteryUsageStartTs, curStart);
-            updateBatteryUsageStatsOnceInternal(0, buf, builder, userIds, batteryStatsInternal);
+            final BatteryUsageStats statsCommit =
+                    updateBatteryUsageStatsOnceInternal(0,
+                            buf,
+                            builder,
+                            userIds,
+                            batteryStatsInternal);
             curDuration += curStart - lastUidBatteryUsageStartTs;
+            try {
+                statsCommit.close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to close a stat");
+            }
         }
         if (needUpdateUidBatteryUsageInWindow && curDuration >= windowSize) {
             // If we do have long enough data for the window, save it.
@@ -648,8 +659,14 @@
                 }
             }
         }
+        try {
+            stats.close();
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to close a stat");
+        }
     }
 
+    // The BatteryUsageStats object MUST BE CLOSED when finished using
     private BatteryUsageStats updateBatteryUsageStatsOnceInternal(long expectedDuration,
             SparseArray<BatteryUsage> buf, BatteryUsageStatsQuery.Builder builder,
             ArraySet<UserHandle> userIds, BatteryStatsInternal batteryStatsInternal) {
@@ -662,7 +679,16 @@
             // Shouldn't happen unless in test.
             return null;
         }
+        // We need the first stat in the list, so we should
+        // close out the others.
         final BatteryUsageStats stats = statsList.get(0);
+        for (int i = 1; i < statsList.size(); i++) {
+            try {
+                statsList.get(i).close();
+            } catch (IOException e) {
+                Slog.w(TAG, "Failed to close a stat in BatteryUsageStats List");
+            }
+        }
         final List<UidBatteryConsumer> uidConsumers = stats.getUidBatteryConsumers();
         if (uidConsumers != null) {
             final long start = stats.getStatsStartTimestamp();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 5fa0ffa..6005b64 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1442,7 +1442,6 @@
             uidRec.setFrozen(false);
             postUidFrozenMessage(uidRec.getUid(), false);
         }
-        reportProcessFreezableChangedLocked(app);
 
         opt.setFreezerOverride(false);
         if (pid == 0 || !opt.isFrozen()) {
@@ -1481,6 +1480,7 @@
         if (processKilled) {
             return;
         }
+        reportProcessFreezableChangedLocked(app);
 
         long freezeTime = opt.getFreezeUnfreezeTime();
 
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index e84fed7..4b622f5 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -173,6 +173,16 @@
                 TextFlags.NAMESPACE, TextFlags.ENABLE_NEW_CONTEXT_MENU,
                 TextFlags.KEY_ENABLE_NEW_CONTEXT_MENU, boolean.class,
                 TextFlags.ENABLE_NEW_CONTEXT_MENU_DEFAULT));
+
+        // Register all text aconfig flags.
+        for (String flag : TextFlags.TEXT_ACONFIGS_FLAGS) {
+            sDeviceConfigEntries.add(new DeviceConfigEntry<Boolean>(
+                    TextFlags.NAMESPACE,
+                    flag,
+                    TextFlags.getKeyForFlag(flag),
+                    boolean.class,
+                    false));  // All aconfig flags are false by default.
+        }
         // add other device configs here...
     }
     private static volatile boolean sDeviceConfigContextEntriesLoaded = false;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 3d11c68..0615ecf 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3087,6 +3087,8 @@
             if (old == proc && proc.isPersistent()) {
                 // We are re-adding a persistent process.  Whatevs!  Just leave it there.
                 Slog.w(TAG, "Re-adding persistent process " + proc);
+                // Ensure that the mCrashing flag is cleared, since this is a restart
+                proc.resetCrashingOnRestart();
             } else if (old != null) {
                 if (old.isKilled()) {
                     // The old process has been killed, we probably haven't had
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index cfbb5a5..d8a2695 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -618,6 +618,10 @@
         mPkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
     }
 
+    void resetCrashingOnRestart() {
+        mErrorState.setCrashing(false);
+    }
+
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     UidRecord getUidRecord() {
         return mUidRecord;
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
new file mode 100644
index 0000000..b03cc62
--- /dev/null
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.am"
+
+flag {
+    name: "oomadjuster_correctness_rewrite"
+    namespace: "android_platform_power_optimization"
+    description: "Utilize new OomAdjuster implementation"
+    bug: "298055811"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 99a5398..debf828 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.display.BrightnessUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.display.utils.Plog;
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 13ee47e..40b2f5a 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -43,14 +43,16 @@
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
             Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig, Handler handler,
-            DisplayManagerFlags flags) {
+            DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
         this(hbmController, modeChangeCallback, displayDeviceConfig,
-                new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags);
+                new HdrClamper(modeChangeCallback::run, new Handler(handler.getLooper())), flags,
+                displayToken, info);
     }
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
             Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
-            HdrClamper hdrClamper, DisplayManagerFlags flags) {
+            HdrClamper hdrClamper, DisplayManagerFlags flags, IBinder displayToken,
+            DisplayDeviceInfo info) {
         mHbmController = hbmController;
         mModeChangeCallback = modeChangeCallback;
         mHdrClamper = hdrClamper;
@@ -60,10 +62,7 @@
             mNormalBrightnessModeController.resetNbmData(
                     displayDeviceConfig.getLuxThrottlingData());
         }
-        if (mUseHdrClamper) {
-            mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
-        }
-
+        updateHdrClamper(info, displayToken, displayDeviceConfig);
     }
 
     void dump(PrintWriter pw) {
@@ -101,13 +100,12 @@
                             displayDeviceConfig::getHdrBrightnessFromSdr);
                 }
         );
-        if (mUseHdrClamper) {
-            mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData());
-        }
+        updateHdrClamper(info, token, displayDeviceConfig);
     }
 
     void stop() {
         mHbmController.stop();
+        mHdrClamper.stop();
     }
 
     void setAutoBrightnessEnabled(int state) {
@@ -151,6 +149,18 @@
         return mHbmController.getTransitionPoint();
     }
 
+    private void updateHdrClamper(DisplayDeviceInfo info, IBinder token,
+            DisplayDeviceConfig displayDeviceConfig) {
+        if (mUseHdrClamper) {
+            DisplayDeviceConfig.HighBrightnessModeData hbmData =
+                    displayDeviceConfig.getHighBrightnessModeData();
+            float minimumHdrPercentOfScreen =
+                    hbmData == null ? -1f : hbmData.minimumHdrPercentOfScreen;
+            mHdrClamper.resetHdrConfig(displayDeviceConfig.getHdrBrightnessData(), info.width,
+                    info.height, minimumHdrPercentOfScreen, token);
+        }
+    }
+
     private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
         if (mUseNbmController) {
             boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 8642fb8..098cb87 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
 import android.util.Slog;
@@ -205,6 +206,24 @@
      */
     public Runnable requestDisplayStateLocked(int state, float brightnessState,
             float sdrBrightnessState) {
+        return requestDisplayStateLocked(state, brightnessState, sdrBrightnessState, null);
+    }
+
+    /**
+     * Sets the display state, if supported.
+     *
+     * @param state The new display state.
+     * @param brightnessState The new display brightnessState.
+     * @param sdrBrightnessState The new display brightnessState for SDR layers.
+     * @param displayOffloadSession {@link DisplayOffloadSession} associated with current device.
+     * @return A runnable containing work to be deferred until after we have exited the critical
+     *     section, or null if none.
+     */
+    public Runnable requestDisplayStateLocked(
+            int state,
+            float brightnessState,
+            float sdrBrightnessState,
+            @Nullable DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) {
         return null;
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9ef84cb..e942c17 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1772,7 +1772,7 @@
         synchronized (mSyncRoot) {
             // main display adapter
             registerDisplayAdapterLocked(mInjector.getLocalDisplayAdapter(mSyncRoot, mContext,
-                    mHandler, mDisplayDeviceRepo));
+                    mHandler, mDisplayDeviceRepo, mFlags));
 
             // Standalone VR devices rely on a virtual display as their primary display for
             // 2D UI. We register virtual display adapter along side the main display adapter
@@ -2093,8 +2093,11 @@
             // Only send a request for display state if display state has already been initialized.
             if (state != Display.STATE_UNKNOWN) {
                 final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
-                return device.requestDisplayStateLocked(state, brightnessPair.brightness,
-                        brightnessPair.sdrBrightness);
+                return device.requestDisplayStateLocked(
+                        state,
+                        brightnessPair.brightness,
+                        brightnessPair.sdrBrightness,
+                        display.getDisplayOffloadSessionLocked());
             }
         }
         return null;
@@ -3183,9 +3186,10 @@
         }
 
         LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
-                                                   Handler handler,
-                                                   DisplayAdapter.Listener displayAdapterListener) {
-            return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
+                Handler handler, DisplayAdapter.Listener displayAdapterListener,
+                DisplayManagerFlags flags) {
+            return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+                    flags);
         }
 
         long getDefaultDisplayDelayTimeout() {
@@ -4806,6 +4810,49 @@
             }
             return displayGroupIds;
         }
+
+        @Override
+        public DisplayManagerInternal.DisplayOffloadSession registerDisplayOffloader(
+                int displayId, @NonNull DisplayManagerInternal.DisplayOffloader displayOffloader) {
+            if (!mFlags.isDisplayOffloadEnabled()) {
+                return null;
+            }
+            synchronized (mSyncRoot) {
+                LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayId);
+                if (logicalDisplay == null) {
+                    Slog.w(TAG, "registering DisplayOffloader: LogicalDisplay for displayId="
+                            + displayId + " is not found. No Op.");
+                    return null;
+                }
+
+                DisplayPowerControllerInterface displayPowerController =
+                        mDisplayPowerControllers.get(logicalDisplay.getDisplayIdLocked());
+                if (displayPowerController == null) {
+                    Slog.w(TAG,
+                            "setting doze state override: DisplayPowerController for displayId="
+                                    + displayId + " is unavailable. No Op.");
+                    return null;
+                }
+
+                DisplayOffloadSession session =
+                        new DisplayOffloadSession() {
+                            @Override
+                            public void setDozeStateOverride(int displayState) {
+                                synchronized (mSyncRoot) {
+                                    displayPowerController.overrideDozeScreenState(displayState);
+                                }
+                            }
+
+                            @Override
+                            public DisplayOffloader getDisplayOffloader() {
+                                return displayOffloader;
+                            }
+                        };
+                logicalDisplay.setDisplayOffloadSessionLocked(session);
+                displayPowerController.setDisplayOffloadSession(session);
+                return session;
+            }
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 83f4df9..ce98559 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -34,6 +34,8 @@
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.metrics.LogMaker;
@@ -588,8 +590,9 @@
             new SparseArray<>();
 
     private boolean mBootCompleted;
-
     private final DisplayManagerFlags mFlags;
+    private int mDozeStateOverride = Display.STATE_UNKNOWN;
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
     /**
      * Creates the display power controller.
@@ -684,7 +687,9 @@
         HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
 
         mBrightnessRangeController = new BrightnessRangeController(hbmController,
-                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+                mDisplayDevice.getDisplayTokenLocked(),
+                mDisplayDevice.getDisplayDeviceInfoLocked());
 
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
@@ -957,6 +962,23 @@
     }
 
     @Override
+    public void overrideDozeScreenState(int displayState) {
+        synchronized (mLock) {
+            if (mDisplayOffloadSession == null ||
+                    !DisplayOffloadSession.isSupportedOffloadState(displayState)) {
+                return;
+            }
+            mDozeStateOverride = displayState;
+            sendUpdatePowerState();
+        }
+    }
+
+    @Override
+    public void setDisplayOffloadSession(DisplayOffloadSession session) {
+        mDisplayOffloadSession = session;
+    }
+
+    @Override
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
         if (mAutomaticBrightnessController == null) {
             return null;
@@ -1518,6 +1540,7 @@
                 } else {
                     state = Display.STATE_DOZE;
                 }
+                state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
                 if (!mAllowAutoBrightnessWhileDozingConfig) {
                     brightnessState = mPowerRequest.dozeScreenBrightness;
                     mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
@@ -1937,6 +1960,7 @@
                 // We want to scale HDR brightness level with the SDR level, we also need to restore
                 // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mBrightnessRangeController.getHdrBrightnessValue();
+                mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
             }
 
             final float currentBrightness = mPowerState.getScreenBrightness();
@@ -3001,6 +3025,7 @@
             pw.println("  mLeadDisplayId=" + mLeadDisplayId);
             pw.println("  mLightSensor=" + mLightSensor);
             pw.println("  mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers);
+            pw.println("  mDozeStateOverride=" + mDozeStateOverride);
 
             pw.println();
             pw.println("Display Power Controller Locked State:");
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index b0d293a..1652871 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -31,6 +31,8 @@
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.metrics.LogMaker;
@@ -470,9 +472,10 @@
             new SparseArray();
 
     private boolean mBootCompleted;
-
     private final DisplayManagerFlags mFlags;
 
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
     /**
      * Creates the display power controller.
      */
@@ -549,7 +552,9 @@
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
         mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
-                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags);
+                modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
+                mDisplayDevice.getDisplayTokenLocked(),
+                mDisplayDevice.getDisplayDeviceInfoLocked());
 
         mDisplayBrightnessController =
                 new DisplayBrightnessController(context, null,
@@ -588,21 +593,24 @@
 
         if (mDisplayId == Display.DEFAULT_DISPLAY) {
             mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
-            boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
-                @Override
-                public void onReduceBrightColorsActivationChanged(boolean activated,
-                        boolean userInitiated) {
-                    applyReduceBrightColorsSplineAdjustment();
+            if (mCdsi != null) {
+                boolean active = mCdsi.setReduceBrightColorsListener(
+                        new ReduceBrightColorsListener() {
+                            @Override
+                            public void onReduceBrightColorsActivationChanged(boolean activated,
+                                    boolean userInitiated) {
+                                applyReduceBrightColorsSplineAdjustment();
 
-                }
+                            }
 
-                @Override
-                public void onReduceBrightColorsStrengthChanged(int strength) {
+                            @Override
+                            public void onReduceBrightColorsStrengthChanged(int strength) {
+                                applyReduceBrightColorsSplineAdjustment();
+                            }
+                        });
+                if (active) {
                     applyReduceBrightColorsSplineAdjustment();
                 }
-            });
-            if (active) {
-                applyReduceBrightColorsSplineAdjustment();
             }
         } else {
             mCdsi = null;
@@ -760,6 +768,24 @@
     }
 
     @Override
+    public void overrideDozeScreenState(int displayState) {
+        mHandler.postAtTime(() -> {
+            if (mDisplayOffloadSession == null
+                    || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
+                    || displayState == Display.STATE_UNKNOWN)) {
+                return;
+            }
+            mDisplayStateController.overrideDozeScreenState(displayState);
+            sendUpdatePowerState();
+        }, mClock.uptimeMillis());
+    }
+
+    @Override
+    public void setDisplayOffloadSession(DisplayOffloadSession session) {
+        mDisplayOffloadSession = session;
+    }
+
+    @Override
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
         if (mAutomaticBrightnessController == null) {
             return null;
@@ -1540,6 +1566,7 @@
                 // SDR brightness immediately when entering dim or low power mode.
                 animateValue = mBrightnessRangeController.getHdrBrightnessValue();
                 customTransitionRate = mBrightnessRangeController.getHdrTransitionRate();
+                mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_HDR);
             }
 
             final float currentBrightness = mPowerState.getScreenBrightness();
@@ -1871,15 +1898,12 @@
 
     private HighBrightnessModeController createHbmControllerLocked(
             HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) {
-        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
-        final IBinder displayToken =
-                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
-        final String displayUniqueId =
-                mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+        final DisplayDeviceConfig ddConfig = mDisplayDevice.getDisplayDeviceConfig();
+        final IBinder displayToken = mDisplayDevice.getDisplayTokenLocked();
+        final String displayUniqueId = mDisplayDevice.getUniqueId();
         final DisplayDeviceConfig.HighBrightnessModeData hbmData =
                 ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
-        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+        final DisplayDeviceInfo info = mDisplayDevice.getDisplayDeviceInfoLocked();
         return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height,
                 displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN,
                 PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) ->
@@ -3025,9 +3049,9 @@
         BrightnessRangeController getBrightnessRangeController(
                 HighBrightnessModeController hbmController, Runnable modeChangeCallback,
                 DisplayDeviceConfig displayDeviceConfig, Handler handler,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
             return new BrightnessRangeController(hbmController,
-                    modeChangeCallback, displayDeviceConfig, handler, flags);
+                    modeChangeCallback, displayDeviceConfig, handler, flags, displayToken, info);
         }
 
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
index e3108c9..181386a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -139,6 +139,10 @@
     boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
             boolean waitForNegativeProximity);
 
+    void overrideDozeScreenState(int displayState);
+
+    void setDisplayOffloadSession(DisplayManagerInternal.DisplayOffloadSession session);
+
     /**
      * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
      * value.
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b32a207..0a1f316 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -22,6 +22,9 @@
 import android.app.ActivityThread;
 import android.content.Context;
 import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.sidekick.SidekickInternal;
 import android.os.Build;
 import android.os.Handler;
@@ -47,6 +50,7 @@
 import com.android.internal.display.BrightnessSynchronizer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.mode.DisplayModeDirector;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
@@ -80,21 +84,24 @@
 
     private final boolean mIsBootDisplayModeSupported;
 
+    private final DisplayManagerFlags mFlags;
+
     private Context mOverlayContext;
 
     // Called with SyncRoot lock held.
     LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
-            Handler handler, Listener listener) {
-        this(syncRoot, context, handler, listener, new Injector());
+            Handler handler, Listener listener, DisplayManagerFlags flags) {
+        this(syncRoot, context, handler, listener, flags, new Injector());
     }
 
     @VisibleForTesting
     LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
-            Listener listener, Injector injector) {
+            Listener listener, DisplayManagerFlags flags, Injector injector) {
         super(syncRoot, context, handler, listener, TAG);
         mInjector = injector;
         mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
         mIsBootDisplayModeSupported = mSurfaceControlProxy.getBootDisplayModeSupport();
+        mFlags = flags;
     }
 
     @Override
@@ -224,6 +231,7 @@
         private boolean mAllmRequested;
         private boolean mGameContentTypeRequested;
         private boolean mSidekickActive;
+        private boolean mDisplayOffloadActive;
         private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
         // The supported display modes according to SurfaceFlinger
         private SurfaceControl.DisplayMode[] mSfDisplayModes;
@@ -746,8 +754,12 @@
         }
 
         @Override
-        public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
-                final float sdrBrightnessState) {
+        public Runnable requestDisplayStateLocked(
+                final int state,
+                final float brightnessState,
+                final float sdrBrightnessState,
+                DisplayOffloadSession displayOffloadSession) {
+
             // Assume that the brightness is off if the display is being turned off.
             assert state != Display.STATE_OFF
                     || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT;
@@ -813,18 +825,39 @@
                                     + ", state=" + Display.stateToString(state) + ")");
                         }
 
-                        // We must tell sidekick to stop controlling the display before we
-                        // can change its power mode, so do that first.
-                        if (mSidekickActive) {
-                            Trace.traceBegin(Trace.TRACE_TAG_POWER,
-                                    "SidekickInternal#endDisplayControl");
-                            try {
-                                mSidekickInternal.endDisplayControl();
-                            } finally {
-                                Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                        DisplayOffloader displayOffloader =
+                                displayOffloadSession == null
+                                        ? null
+                                        : displayOffloadSession.getDisplayOffloader();
+
+                        boolean isDisplayOffloadEnabled = mFlags.isDisplayOffloadEnabled();
+
+                        // We must tell sidekick/displayoffload to stop controlling the display
+                        // before we can change its power mode, so do that first.
+                        if (isDisplayOffloadEnabled) {
+                            if (mDisplayOffloadActive && displayOffloader != null) {
+                                Trace.traceBegin(Trace.TRACE_TAG_POWER,
+                                        "DisplayOffloader#stopOffload");
+                                try {
+                                    displayOffloader.stopOffload();
+                                } finally {
+                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                                }
+                                mDisplayOffloadActive = false;
                             }
-                            mSidekickActive = false;
+                        } else {
+                            if (mSidekickActive) {
+                                Trace.traceBegin(Trace.TRACE_TAG_POWER,
+                                        "SidekickInternal#endDisplayControl");
+                                try {
+                                    mSidekickInternal.endDisplayControl();
+                                } finally {
+                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                                }
+                                mSidekickActive = false;
+                            }
                         }
+
                         final int mode = getPowerModeForState(state);
                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayState("
                                 + "id=" + physicalDisplayId
@@ -836,16 +869,32 @@
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
                         setCommittedState(state);
+
                         // If we're entering a suspended (but not OFF) power state and we
-                        // have a sidekick available, tell it now that it can take control.
-                        if (Display.isSuspendedState(state) && state != Display.STATE_OFF
-                                && mSidekickInternal != null && !mSidekickActive) {
-                            Trace.traceBegin(Trace.TRACE_TAG_POWER,
-                                    "SidekickInternal#startDisplayControl");
-                            try {
-                                mSidekickActive = mSidekickInternal.startDisplayControl(state);
-                            } finally {
-                                Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                        // have a sidekick/displayoffload available, tell it now that it can take
+                        // control.
+                        if (isDisplayOffloadEnabled) {
+                            if (DisplayOffloadSession.isSupportedOffloadState(state) &&
+                                    displayOffloader != null
+                                    && !mDisplayOffloadActive) {
+                                Trace.traceBegin(
+                                        Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
+                                try {
+                                    mDisplayOffloadActive = displayOffloader.startOffload();
+                                } finally {
+                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                                }
+                            }
+                        } else {
+                            if (Display.isSuspendedState(state) && state != Display.STATE_OFF
+                                    && mSidekickInternal != null && !mSidekickActive) {
+                                Trace.traceBegin(Trace.TRACE_TAG_POWER,
+                                        "SidekickInternal#startDisplayControl");
+                                try {
+                                    mSidekickActive = mSidekickInternal.startDisplayControl(state);
+                                } finally {
+                                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
+                                }
                             }
                         }
                     }
@@ -858,6 +907,7 @@
                         }
                     }
 
+
                     private void setDisplayBrightness(float brightnessState,
                             float sdrBrightnessState) {
                         // brightnessState includes invalid, off and full range.
@@ -1345,6 +1395,7 @@
 
     public interface DisplayEventListener {
         void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
+        void onHotplugConnectionError(long timestampNanos, int connectionError);
         void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
                 long renderPeriod);
         void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
@@ -1367,6 +1418,11 @@
         }
 
         @Override
+        public void onHotplugConnectionError(long timestampNanos, int errorCode) {
+            mListener.onHotplugConnectionError(timestampNanos, errorCode);
+        }
+
+        @Override
         public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
                 long renderPeriod) {
             mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod);
@@ -1392,6 +1448,15 @@
         }
 
         @Override
+        public void onHotplugConnectionError(long timestampNanos, int connectionError) {
+            if (DEBUG) {
+                Slog.d(TAG, "onHotplugConnectionError("
+                        + "timestampNanos=" + timestampNanos
+                        + ", connectionError=" + connectionError + ")");
+            }
+        }
+
+        @Override
         public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId,
                 long renderPeriod) {
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d4d104e..bd82b81 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -137,6 +137,9 @@
     private final Rect mTempLayerStackRect = new Rect();
     private final Rect mTempDisplayRect = new Rect();
 
+    /** A session token that controls the offloading operations of this logical display. */
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
     /**
      * Name of a display group to which the display is assigned.
      */
@@ -941,6 +944,15 @@
         return mDisplayGroupName;
     }
 
+    public void setDisplayOffloadSessionLocked(
+            DisplayManagerInternal.DisplayOffloadSession session) {
+        mDisplayOffloadSession = session;
+    }
+
+    public DisplayManagerInternal.DisplayOffloadSession getDisplayOffloadSessionLocked() {
+        return mDisplayOffloadSession;
+    }
+
     public void dumpLocked(PrintWriter pw) {
         pw.println("mDisplayId=" + mDisplayId);
         pw.println("mIsEnabled=" + mIsEnabled);
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 5ba042c..e38c2c5 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -20,6 +20,8 @@
 import android.util.FloatProperty;
 import android.view.Choreographer;
 
+import com.android.internal.display.BrightnessUtils;
+
 /**
  * A custom animator that progressively updates a property value at
  * a given variable rate until it reaches a particular target value.
diff --git a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
index a514136..e46edd9 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/HdrClamper.java
@@ -17,9 +17,13 @@
 package com.android.server.display.brightness.clamper;
 
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.PowerManager;
+import android.view.SurfaceControlHdrLayerInfoListener;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.config.HdrBrightnessData;
 
 import java.io.PrintWriter;
@@ -33,11 +37,18 @@
 
     private final Runnable mDebouncer;
 
+    private final HdrLayerInfoListener mHdrListener;
+
     @Nullable
     private HdrBrightnessData mHdrBrightnessData = null;
 
+    @Nullable
+    private IBinder mRegisteredDisplayToken = null;
+
     private float mAmbientLux = Float.MAX_VALUE;
 
+    private boolean mHdrVisible = false;
+
     private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
     private float mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
 
@@ -47,6 +58,12 @@
 
     public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
             Handler handler) {
+        this(clamperChangeListener, handler, new Injector());
+    }
+
+    @VisibleForTesting
+    public HdrClamper(BrightnessClamperController.ClamperChangeListener clamperChangeListener,
+            Handler handler, Injector injector) {
         mClamperChangeListener = clamperChangeListener;
         mHandler = handler;
         mDebouncer = () -> {
@@ -54,6 +71,10 @@
             mMaxBrightness = mDesiredMaxBrightness;
             mClamperChangeListener.onChanged();
         };
+        mHdrListener = injector.getHdrListener((visible) -> {
+            mHdrVisible = visible;
+            recalculateBrightnessCap(mHdrBrightnessData, mAmbientLux, mHdrVisible);
+        }, handler);
     }
 
     // Called in same looper: mHandler.getLooper()
@@ -72,16 +93,37 @@
      */
     public void onAmbientLuxChange(float ambientLux) {
         mAmbientLux = ambientLux;
-        recalculateBrightnessCap(mHdrBrightnessData, ambientLux);
+        recalculateBrightnessCap(mHdrBrightnessData, ambientLux, mHdrVisible);
     }
 
     /**
      * Updates brightness cap config.
      * Called in same looper: mHandler.getLooper()
      */
-    public void resetHdrConfig(HdrBrightnessData data) {
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public void resetHdrConfig(HdrBrightnessData data, int width, int height,
+            float minimumHdrPercentOfScreen, IBinder displayToken) {
         mHdrBrightnessData = data;
-        recalculateBrightnessCap(data, mAmbientLux);
+        mHdrListener.mHdrMinPixels = (float) (width * height) * minimumHdrPercentOfScreen;
+        if (displayToken != mRegisteredDisplayToken) { // token changed, resubscribe
+            if (mRegisteredDisplayToken != null) { // previous token not null, unsubscribe
+                mHdrListener.unregister(mRegisteredDisplayToken);
+                mHdrVisible = false;
+            }
+            if (displayToken != null) { // new token not null, subscribe
+                mHdrListener.register(displayToken);
+            }
+            mRegisteredDisplayToken = displayToken;
+        }
+        recalculateBrightnessCap(data, mAmbientLux, mHdrVisible);
+    }
+
+    /** Clean up all resources */
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    public void stop() {
+        if (mRegisteredDisplayToken != null) {
+            mHdrListener.unregister(mRegisteredDisplayToken);
+        }
     }
 
     /**
@@ -98,13 +140,28 @@
         pw.println("  mAmbientLux=" + mAmbientLux);
     }
 
-    private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux) {
-        if (data == null) {
-            mHandler.removeCallbacks(mDebouncer);
+    private void reset() {
+        if (mMaxBrightness == PowerManager.BRIGHTNESS_MAX
+                && mDesiredMaxBrightness == PowerManager.BRIGHTNESS_MAX && mTransitionRate == -1f
+                && mDesiredTransitionRate == -1f) { // already done reset, do nothing
             return;
         }
-        float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
+        mHandler.removeCallbacks(mDebouncer);
+        mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+        mDesiredMaxBrightness = PowerManager.BRIGHTNESS_MAX;
+        mDesiredTransitionRate = -1f;
+        mTransitionRate = 1f;
+        mClamperChangeListener.onChanged();
+    }
 
+    private void recalculateBrightnessCap(HdrBrightnessData data, float ambientLux,
+            boolean hdrVisible) {
+        if (data == null || !hdrVisible) {
+            reset();
+            return;
+        }
+
+        float expectedMaxBrightness = findBrightnessLimit(data, ambientLux);
         if (mMaxBrightness == expectedMaxBrightness) {
             mDesiredMaxBrightness = mMaxBrightness;
             mDesiredTransitionRate = -1f;
@@ -127,6 +184,8 @@
             mHandler.removeCallbacks(mDebouncer);
             mHandler.postDelayed(mDebouncer, debounceTime);
         }
+        // do nothing if expectedMaxBrightness == mDesiredMaxBrightness
+        // && expectedMaxBrightness != mMaxBrightness
     }
 
     private float findBrightnessLimit(HdrBrightnessData data, float ambientLux) {
@@ -143,4 +202,36 @@
         }
         return foundMaxBrightness;
     }
+
+    @FunctionalInterface
+    interface HdrListener {
+        void onHdrVisible(boolean visible);
+    }
+
+    static class HdrLayerInfoListener extends SurfaceControlHdrLayerInfoListener {
+        private final HdrListener mHdrListener;
+
+        private final Handler mHandler;
+
+        private float mHdrMinPixels = Float.MAX_VALUE;
+
+        HdrLayerInfoListener(HdrListener hdrListener, Handler handler) {
+            mHdrListener = hdrListener;
+            mHandler = handler;
+        }
+
+        @Override
+        public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers, int maxW,
+                int maxH, int flags, float maxDesiredHdrSdrRatio) {
+            mHandler.post(() ->
+                    mHdrListener.onHdrVisible(
+                            numberOfHdrLayers > 0 && (float) (maxW * maxH) >= mHdrMinPixels));
+        }
+    }
+
+    static class Injector {
+        HdrLayerInfoListener getHdrListener(HdrListener hdrListener, Handler handler) {
+            return new HdrLayerInfoListener(hdrListener, handler);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index ff768d6..b6273e1 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -47,6 +47,10 @@
             Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
             Flags::enableAdaptiveToneImprovements1);
 
+    private final FlagState mDisplayOffloadFlagState = new FlagState(
+            Flags.FLAG_ENABLE_DISPLAY_OFFLOAD,
+            Flags::enableDisplayOffload);
+
     private final FlagState mDisplayResolutionRangeVotingState = new FlagState(
             Flags.FLAG_ENABLE_DISPLAY_RESOLUTION_RANGE_VOTING,
             Flags::enableDisplayResolutionRangeVoting);
@@ -111,6 +115,11 @@
         return mDisplaysRefreshRatesSynchronizationState.isEnabled();
     }
 
+    /** Returns whether displayoffload is enabled on not */
+    public boolean isDisplayOffloadEnabled() {
+        return mDisplayOffloadFlagState.isEnabled();
+    }
+
     private static class FlagState {
 
         private final String mName;
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index a5b8cbb..542f26c 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -63,5 +63,12 @@
     namespace: "display_manager"
     description: "Enables synchronization of refresh rates across displays"
     bug: "294015845"
+}
+
+flag {
+    name: "enable_display_offload"
+    namespace: "display_manager"
+    description: "Feature flag for DisplayOffload"
+    bug: "299521647"
     is_fixed_read_only: true
 }
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index b1a1c60..5d6e650 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -32,6 +32,7 @@
 public class DisplayStateController {
     private DisplayPowerProximityStateController mDisplayPowerProximityStateController;
     private boolean mPerformScreenOffTransition = false;
+    private int mDozeStateOverride = Display.STATE_UNKNOWN;
 
     public DisplayStateController(DisplayPowerProximityStateController
             displayPowerProximityStateController) {
@@ -65,6 +66,7 @@
                 } else {
                     state = Display.STATE_DOZE;
                 }
+                state = mDozeStateOverride == Display.STATE_UNKNOWN ? state : mDozeStateOverride;
                 break;
             case DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM:
             case DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT:
@@ -84,6 +86,10 @@
         return state;
     }
 
+    public void overrideDozeScreenState(int displayState) {
+        mDozeStateOverride = displayState;
+    }
+
     /**
      * Checks if the screen off transition is to be performed or not.
      */
@@ -100,6 +106,8 @@
         pw.println();
         pw.println("DisplayStateController:");
         pw.println("  mPerformScreenOffTransition:" + mPerformScreenOffTransition);
+        pw.println("  mDozeStateOverride=" + mDozeStateOverride);
+
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
         if (mDisplayPowerProximityStateController != null) {
             mDisplayPowerProximityStateController.dumpLocal(ipw);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c3abfc1..f168f43 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -349,17 +349,17 @@
 
         @Override
         public void onUserStarting(@NonNull TargetUser user) {
-            mLockSettingsService.onStartUser(user.getUserIdentifier());
+            mLockSettingsService.onUserStarting(user.getUserIdentifier());
         }
 
         @Override
         public void onUserUnlocking(@NonNull TargetUser user) {
-            mLockSettingsService.onUnlockUser(user.getUserIdentifier());
+            mLockSettingsService.onUserUnlocking(user.getUserIdentifier());
         }
 
         @Override
         public void onUserStopped(@NonNull TargetUser user) {
-            mLockSettingsService.onCleanupUser(user.getUserIdentifier());
+            mLockSettingsService.onUserStopped(user.getUserIdentifier());
         }
     }
 
@@ -784,7 +784,7 @@
     }
 
     @VisibleForTesting
-    void onCleanupUser(int userId) {
+    void onUserStopped(int userId) {
         hideEncryptionNotification(new UserHandle(userId));
         // User is stopped with its CE key evicted. Restore strong auth requirement to the default
         // flags after boot since stopping and restarting a user later is equivalent to rebooting
@@ -796,7 +796,7 @@
         }
     }
 
-    private void onStartUser(final int userId) {
+    private void onUserStarting(final int userId) {
         maybeShowEncryptionNotificationForUser(userId, "user started");
     }
 
@@ -832,7 +832,7 @@
         }
     }
 
-    private void onUnlockUser(final int userId) {
+    private void onUserUnlocking(final int userId) {
         // Perform tasks which require locks in LSS on a handler, as we are callbacks from
         // ActivityManager.unlockUser()
         mHandler.post(new Runnable() {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4892c22..83a3125 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@
 import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
 import static android.media.MediaRouter2Utils.getOriginalId;
 import static android.media.MediaRouter2Utils.getProviderId;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.media.MediaFeatureFlagManager.FEATURE_SCANNING_MINIMUM_PACKAGE_IMPORTANCE;
 
@@ -487,12 +488,13 @@
 
         final int callerUid = Binder.getCallingUid();
         final int callerPid = Binder.getCallingPid();
-        final int userId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
+        final int callerUserId = UserHandle.getUserHandleForUid(callerUid).getIdentifier();
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                registerManagerLocked(manager, callerUid, callerPid, callerPackageName, userId);
+                registerManagerLocked(
+                        manager, callerUid, callerPid, callerPackageName, callerUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1156,8 +1158,12 @@
     }
 
     @GuardedBy("mLock")
-    private void registerManagerLocked(@NonNull IMediaRouter2Manager manager,
-            int callerUid, int callerPid, @NonNull String callerPackageName, int userId) {
+    private void registerManagerLocked(
+            @NonNull IMediaRouter2Manager manager,
+            int callerUid,
+            int callerPid,
+            @NonNull String callerPackageName,
+            int callerUserId) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
@@ -1167,14 +1173,17 @@
             return;
         }
 
-        Slog.i(TAG, TextUtils.formatSimple(
-                "registerManager | callerUid: %d, callerPid: %d, package: %s, user: %d",
-                callerUid, callerPid, callerPackageName, userId));
+        Slog.i(
+                TAG,
+                TextUtils.formatSimple(
+                        "registerManager | callerUid: %d, callerPid: %d, callerPackage: %s,"
+                            + " callerUserId: %d",
+                        callerUid, callerPid, callerPackageName, callerUserId));
 
         mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, callerPid, callerUid,
                 "Must hold MEDIA_CONTENT_CONTROL permission.");
 
-        UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+        UserRecord userRecord = getOrCreateUserRecordLocked(callerUserId);
         managerRecord = new ManagerRecord(
                 userRecord, manager, callerUid, callerPid, callerPackageName);
         try {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c6f6fe2..fe91050 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1021,7 +1021,7 @@
         synchronized (mSnoozing) {
             mSnoozing.remove(user);
         }
-        rebindServices(true, user);
+        unbindUserServices(user);
     }
 
     public void onUserSwitched(int user) {
@@ -1408,12 +1408,24 @@
     void unbindOtherUserServices(int currentUser) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("ManagedServices.unbindOtherUserServices_current" + currentUser);
-        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+        unbindServicesImpl(currentUser, true /* allExceptUser */);
+        t.traceEnd();
+    }
 
+    void unbindUserServices(int user) {
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+        t.traceBegin("ManagedServices.unbindUserServices" + user);
+        unbindServicesImpl(user, false /* allExceptUser */);
+        t.traceEnd();
+    }
+
+    void unbindServicesImpl(int user, boolean allExceptUser) {
+        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
         synchronized (mMutex) {
             final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
             for (ManagedServiceInfo info : removableBoundServices) {
-                if (info.userid != currentUser) {
+                if ((allExceptUser && (info.userid != user))
+                        || (!allExceptUser && (info.userid == user))) {
                     Set<ComponentName> toUnbind =
                             componentsToUnbind.get(info.userid, new ArraySet<>());
                     toUnbind.add(info.component);
@@ -1422,7 +1434,6 @@
             }
         }
         unbindFromServices(componentsToUnbind);
-        t.traceEnd();
     }
 
     protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 802dfb1..a3c71c2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -177,6 +177,7 @@
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
@@ -263,6 +264,7 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -4159,7 +4161,7 @@
                 String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mPreferencesHelper.getNotificationChannelGroups(
-                    pkg, Binder.getCallingUid(), false, false, true);
+                    pkg, Binder.getCallingUid(), false, false, true, true, null);
         }
 
         @Override
@@ -4280,7 +4282,36 @@
                 String pkg, int uid, boolean includeDeleted) {
             enforceSystemOrSystemUI("getNotificationChannelGroupsForPackage");
             return mPreferencesHelper.getNotificationChannelGroups(
-                    pkg, uid, includeDeleted, true, false);
+                    pkg, uid, includeDeleted, true, false, true, null);
+        }
+
+        @Override
+        public ParceledListSlice<NotificationChannelGroup>
+                getRecentBlockedNotificationChannelGroupsForPackage(String pkg, int uid) {
+            enforceSystemOrSystemUI("getRecentBlockedNotificationChannelGroupsForPackage");
+            Set<String> recentlySentChannels = new HashSet<>();
+            long now = System.currentTimeMillis();
+            long startTime = now - (DateUtils.DAY_IN_MILLIS * 14);
+            UsageEvents events = mUsageStatsManagerInternal.queryEventsForUser(
+                UserHandle.getUserId(uid),  startTime, now, UsageEvents.SHOW_ALL_EVENT_DATA);
+            // get all channelids that sent notifs in the past 2 weeks
+            if (events != null) {
+                UsageEvents.Event event = new UsageEvents.Event();
+                while (events.hasNextEvent()) {
+                    events.getNextEvent(event);
+                    if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+                        if (pkg.equals(event.mPackage)) {
+                            String channelId = event.mNotificationChannelId;
+                            if (channelId != null) {
+                                recentlySentChannels.add(channelId);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return mPreferencesHelper.getNotificationChannelGroups(
+                    pkg, uid, false, true, false, true, recentlySentChannels);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b132a23..de698d9 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1459,9 +1459,9 @@
         }
     }
 
-    @Override
     public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
+            int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty,
+            boolean includeBlocked, Set<String> activeChannelFilter) {
         Objects.requireNonNull(pkg);
         Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
         synchronized (mPackagePreferences) {
@@ -1473,7 +1473,11 @@
             int N = r.channels.size();
             for (int i = 0; i < N; i++) {
                 final NotificationChannel nc = r.channels.valueAt(i);
-                if (includeDeleted || !nc.isDeleted()) {
+                boolean includeChannel = (includeDeleted || !nc.isDeleted())
+                        && (activeChannelFilter == null
+                                || (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
+                                || activeChannelFilter.contains(nc.getId()));
+                if (includeChannel) {
                     if (nc.getGroup() != null) {
                         if (r.groups.get(nc.getGroup()) != null) {
                             NotificationChannelGroup ncg = groups.get(nc.getGroup());
@@ -1481,7 +1485,6 @@
                                 ncg = r.groups.get(nc.getGroup()).clone();
                                 ncg.setChannels(new ArrayList<>());
                                 groups.put(nc.getGroup(), ncg);
-
                             }
                             ncg.addChannel(nc);
                         }
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index fec3591..8df24c9 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -40,8 +40,6 @@
             int uid);
     void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromTargetApp, int callingUid, boolean isSystemOrSystemUi);
-    ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
-            int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty);
     boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp, boolean hasDndAccess, int callingUid,
             boolean isSystemOrSystemUi);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 52eef47..b9464d9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -18,9 +18,6 @@
 
 import static android.app.AppGlobals.getPackageManager;
 import static android.content.Intent.ACTION_OVERLAY_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -31,10 +28,10 @@
 import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
 import static android.content.om.OverlayManagerTransaction.Request.TYPE_UNREGISTER_FABRICATED;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
+import static android.os.Process.INVALID_UID;
 import static android.os.Trace.TRACE_TAG_RRO;
 import static android.os.Trace.traceBegin;
 import static android.os.Trace.traceEnd;
-
 import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
 
 import android.annotation.NonNull;
@@ -82,6 +79,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
@@ -261,6 +259,8 @@
 
     private final OverlayActorEnforcer mActorEnforcer;
 
+    private final PackageMonitor mPackageMonitor = new OverlayManagerPackageMonitor();
+
     private int mPrevStartedUserId = -1;
 
     public OverlayManagerService(@NonNull final Context context) {
@@ -277,16 +277,9 @@
                     OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
             mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
 
-            HandlerThread packageReceiverThread = new HandlerThread(TAG);
-            packageReceiverThread.start();
-
-            final IntentFilter packageFilter = new IntentFilter();
-            packageFilter.addAction(ACTION_PACKAGE_ADDED);
-            packageFilter.addAction(ACTION_PACKAGE_CHANGED);
-            packageFilter.addAction(ACTION_PACKAGE_REMOVED);
-            packageFilter.addDataScheme("package");
-            getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, packageReceiverThread.getThreadHandler());
+            HandlerThread packageMonitorThread = new HandlerThread(TAG);
+            packageMonitorThread.start();
+            mPackageMonitor.register(context, packageMonitorThread.getLooper(), true);
 
             final IntentFilter userFilter = new IntentFilter();
             userFilter.addAction(ACTION_USER_ADDED);
@@ -372,166 +365,171 @@
         return defaultPackages.toArray(new String[defaultPackages.size()]);
     }
 
-    private final class PackageReceiver extends BroadcastReceiver {
+    private final class OverlayManagerPackageMonitor extends PackageMonitor {
+
         @Override
-        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
-            final String action = intent.getAction();
-            if (action == null) {
-                Slog.e(TAG, "Cannot handle package broadcast with null action");
-                return;
-            }
-            final Uri data = intent.getData();
-            if (data == null) {
-                Slog.e(TAG, "Cannot handle package broadcast with null data");
-                return;
-            }
-            final String packageName = data.getSchemeSpecificPart();
-
-            final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-            final boolean systemUpdateUninstall =
-                    intent.getBooleanExtra(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
-
-            final int[] userIds;
-            final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL);
-            if (extraUid == UserHandle.USER_NULL) {
-                userIds = mUserManager.getUserIds();
-            } else {
-                userIds = new int[] { UserHandle.getUserId(extraUid) };
-            }
-
-            switch (action) {
-                case ACTION_PACKAGE_ADDED:
-                    if (replacing) {
-                        onPackageReplaced(packageName, userIds);
-                    } else {
-                        onPackageAdded(packageName, userIds);
-                    }
-                    break;
-                case ACTION_PACKAGE_CHANGED:
-                    // ignore the intent if it was sent by the package manager as a result of the
-                    // overlay manager having sent ACTION_OVERLAY_CHANGED
-                    if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
-                        onPackageChanged(packageName, userIds);
-                    }
-                    break;
-                case ACTION_PACKAGE_REMOVED:
-                    if (replacing) {
-                        onPackageReplacing(packageName, systemUpdateUninstall, userIds);
-                    } else {
-                        onPackageRemoved(packageName, userIds);
-                    }
-                    break;
-                default:
-                    // do nothing
-                    break;
-            }
+        public void onPackageAppearedWithExtras(String packageName, Bundle extras) {
+            handlePackageAdd(packageName, extras);
         }
 
-        private void onPackageAdded(@NonNull final String packageName,
-                @NonNull final int[] userIds) {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
-                for (final int userId : userIds) {
-                    synchronized (mLock) {
-                        var packageState = mPackageManager.onPackageAdded(packageName, userId);
-                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
-                                userId)) {
-                            try {
-                                updateTargetPackagesLocked(
-                                        mImpl.onPackageAdded(packageName, userId));
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageAdded internal error", e);
-                            }
+        @Override
+        public void onPackageChangedWithExtras(String packageName, Bundle extras) {
+            handlePackageChange(packageName, extras);
+        }
+
+        @Override
+        public void onPackageDisappearedWithExtras(String packageName, Bundle extras) {
+            handlePackageRemove(packageName, extras);
+        }
+    }
+
+    private int[] getUserIds(int uid) {
+        final int[] userIds;
+        if (uid == INVALID_UID) {
+            userIds = mUserManager.getUserIds();
+        } else {
+            userIds = new int[] { UserHandle.getUserId(uid) };
+        }
+        return userIds;
+    }
+
+    private void handlePackageAdd(String packageName, Bundle extras) {
+        final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+        final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+        final int[] userIds = getUserIds(uid);
+        if (replacing) {
+            onPackageReplaced(packageName, userIds);
+        } else {
+            onPackageAdded(packageName, userIds);
+        }
+    }
+
+    private void handlePackageChange(String packageName, Bundle extras) {
+        final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+        final int[] userIds = getUserIds(uid);
+        if (!ACTION_OVERLAY_CHANGED.equals(extras.getString(EXTRA_REASON))) {
+            onPackageChanged(packageName, userIds);
+        }
+    }
+
+    private void handlePackageRemove(String packageName, Bundle extras) {
+        final boolean replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
+        final boolean systemUpdateUninstall =
+                extras.getBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, false);
+        final int uid = extras.getInt(Intent.EXTRA_UID, 0);
+        final int[] userIds = getUserIds(uid);
+
+        if (replacing) {
+            onPackageReplacing(packageName, systemUpdateUninstall, userIds);
+        } else {
+            onPackageRemoved(packageName, userIds);
+        }
+    }
+
+    private void onPackageAdded(@NonNull final String packageName,
+            @NonNull final int[] userIds) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
+            for (final int userId : userIds) {
+                synchronized (mLock) {
+                    var packageState = mPackageManager.onPackageAdded(packageName, userId);
+                    if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                            userId)) {
+                        try {
+                            updateTargetPackagesLocked(
+                                    mImpl.onPackageAdded(packageName, userId));
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageAdded internal error", e);
                         }
                     }
                 }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
             }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
         }
+    }
 
-        private void onPackageChanged(@NonNull final String packageName,
-                @NonNull final int[] userIds) {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
-                for (int userId : userIds) {
-                    synchronized (mLock) {
-                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
-                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
-                                userId)) {
-                            try {
-                                updateTargetPackagesLocked(
-                                        mImpl.onPackageChanged(packageName, userId));
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageChanged internal error", e);
-                            }
+    private void onPackageChanged(@NonNull final String packageName,
+            @NonNull final int[] userIds) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
+            for (int userId : userIds) {
+                synchronized (mLock) {
+                    var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                    if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                            userId)) {
+                        try {
+                            updateTargetPackagesLocked(
+                                    mImpl.onPackageChanged(packageName, userId));
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageChanged internal error", e);
                         }
                     }
                 }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
             }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
         }
+    }
 
-        private void onPackageReplacing(@NonNull final String packageName,
-                boolean systemUpdateUninstall, @NonNull final int[] userIds) {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
-                for (int userId : userIds) {
-                    synchronized (mLock) {
-                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
-                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
-                                userId)) {
-                            try {
-                                updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
-                                        systemUpdateUninstall, userId));
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageReplacing internal error", e);
-                            }
+    private void onPackageReplacing(@NonNull final String packageName,
+            boolean systemUpdateUninstall, @NonNull final int[] userIds) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
+            for (int userId : userIds) {
+                synchronized (mLock) {
+                    var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                    if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                            userId)) {
+                        try {
+                            updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
+                                    systemUpdateUninstall, userId));
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageReplacing internal error", e);
                         }
                     }
                 }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
             }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
         }
+    }
 
-        private void onPackageReplaced(@NonNull final String packageName,
-                @NonNull final int[] userIds) {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
-                for (int userId : userIds) {
-                    synchronized (mLock) {
-                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
-                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
-                                userId)) {
-                            try {
-                                updateTargetPackagesLocked(
-                                        mImpl.onPackageReplaced(packageName, userId));
-                            } catch (OperationFailedException e) {
-                                Slog.e(TAG, "onPackageReplaced internal error", e);
-                            }
+    private void onPackageReplaced(@NonNull final String packageName,
+            @NonNull final int[] userIds) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
+            for (int userId : userIds) {
+                synchronized (mLock) {
+                    var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                    if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                            userId)) {
+                        try {
+                            updateTargetPackagesLocked(
+                                    mImpl.onPackageReplaced(packageName, userId));
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageReplaced internal error", e);
                         }
                     }
                 }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
             }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
         }
+    }
 
-        private void onPackageRemoved(@NonNull final String packageName,
-                @NonNull final int[] userIds) {
-            try {
-                traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
-                for (int userId : userIds) {
-                    synchronized (mLock) {
-                        mPackageManager.onPackageRemoved(packageName, userId);
-                        updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
-                    }
+    private void onPackageRemoved(@NonNull final String packageName,
+            @NonNull final int[] userIds) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#onPackageRemoved " + packageName);
+            for (int userId : userIds) {
+                synchronized (mLock) {
+                    mPackageManager.onPackageRemoved(packageName, userId);
+                    updateTargetPackagesLocked(mImpl.onPackageRemoved(packageName, userId));
                 }
-            } finally {
-                traceEnd(TRACE_TAG_RRO);
             }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
         }
     }
 
@@ -684,7 +682,7 @@
                     synchronized (mLock) {
                         try {
                             mImpl.setEnabledExclusive(
-                                    overlay, false /* withinCategory */, realUserId)
+                                            overlay, false /* withinCategory */, realUserId)
                                     .ifPresent(
                                             OverlayManagerService.this::updateTargetPackagesLocked);
                             return true;
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index 9a69d77..e367609 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -17,9 +17,14 @@
 package com.android.server.pm;
 
 import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
+import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
 import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.SYSTEM_UID;
 import static android.safetylabel.SafetyLabelConstants.SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
 import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.pm.PackageManagerService.TAG;
@@ -28,6 +33,7 @@
 import android.annotation.AppIdInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.BroadcastOptions;
@@ -38,12 +44,18 @@
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.PowerExemptionManager;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.provider.DeviceConfig;
+import android.stats.storage.StorageEnums;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
@@ -51,10 +63,15 @@
 import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.function.BiFunction;
-import java.util.function.Supplier;
 
 /**
  * Helper class to send broadcasts for various situations.
@@ -70,14 +87,20 @@
     private final UserManagerInternal mUmInternal;
     private final ActivityManagerInternal mAmInternal;
     private final Context mContext;
+    private final Handler mHandler;
+    private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;
+    private final AppsFilterSnapshot mAppsFilter;
 
     BroadcastHelper(PackageManagerServiceInjector injector) {
         mUmInternal = injector.getUserManagerInternal();
         mAmInternal = injector.getActivityManagerInternal();
         mContext = injector.getContext();
+        mHandler = injector.getHandler();
+        mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
+        mAppsFilter = injector.getAppsFilter();
     }
 
-    public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+    void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
             final int[] userIds, int[] instantUserIds,
             @Nullable SparseArray<int[]> broadcastAllowList,
@@ -114,9 +137,16 @@
      * the system and applications allowed to see instant applications to receive package
      * lifecycle events for instant applications.
      */
-    public void doSendBroadcast(String action, String pkg, Bundle extras,
-            int flags, String targetPkg, IIntentReceiver finishedReceiver,
-            int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
+    private void doSendBroadcast(
+            @NonNull String action,
+            @Nullable String pkg,
+            @Nullable Bundle extras,
+            int flags,
+            @Nullable String targetPkg,
+            @Nullable IIntentReceiver finishedReceiver,
+            @NonNull int[] userIds,
+            boolean isInstantApp,
+            @Nullable SparseArray<int[]> broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
             @Nullable Bundle bOptions) {
         for (int userId : userIds) {
@@ -166,9 +196,11 @@
         }
     }
 
-    public void sendResourcesChangedBroadcast(@NonNull Supplier<Computer> snapshotComputer,
-            boolean mediaStatus, boolean replacing, @NonNull String[] pkgNames,
-            @NonNull int[] uids) {
+    void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
+                                       boolean mediaStatus,
+                                       boolean replacing,
+                                       @NonNull String[] pkgNames,
+                                       @NonNull int[] uids) {
         if (ArrayUtils.isEmpty(pkgNames) || ArrayUtils.isEmpty(uids)) {
             return;
         }
@@ -184,7 +216,7 @@
                 null /* targetPkg */, null /* finishedReceiver */, null /* userIds */,
                 null /* instantUserIds */, null /* broadcastAllowList */,
                 (callingUid, intentExtras) -> filterExtrasChangedPackageList(
-                        snapshotComputer.get(), callingUid, intentExtras),
+                        snapshot, callingUid, intentExtras),
                 null /* bOptions */);
     }
 
@@ -193,8 +225,9 @@
      * automatically without needing an explicit launch.
      * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
      */
-    public void sendBootCompletedBroadcastToSystemApp(
-            String packageName, boolean includeStopped, int userId) {
+    private void sendBootCompletedBroadcastToSystemApp(@NonNull String packageName,
+                                                       boolean includeStopped,
+                                                       int userId) {
         // If user is not running, the app didn't miss any broadcast
         if (!mUmInternal.isUserRunning(userId)) {
             return;
@@ -229,7 +262,7 @@
         }
     }
 
-    public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
+    private @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
             @PowerExemptionManager.ReasonCode int reasonCode) {
         long duration = 10_000;
         if (mAmInternal != null) {
@@ -242,9 +275,14 @@
         return bOptions;
     }
 
-    public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
-            ArrayList<String> componentNames, int packageUid, String reason,
-            int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+    private void sendPackageChangedBroadcast(@NonNull String packageName,
+                                             boolean dontKillApp,
+                                             @NonNull ArrayList<String> componentNames,
+                                             int packageUid,
+                                             @Nullable String reason,
+                                             @Nullable int[] userIds,
+                                             @Nullable int[] instantUserIds,
+                                             @Nullable SparseArray<int[]> broadcastAllowList) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
                     + componentNames);
@@ -269,7 +307,7 @@
                 null /* bOptions */);
     }
 
-    public static void sendDeviceCustomizationReadyBroadcast() {
+    static void sendDeviceCustomizationReadyBroadcast() {
         final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
         intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         final IActivityManager am = ActivityManager.getService();
@@ -285,15 +323,23 @@
         }
     }
 
-    public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
-            int launcherUid, @Nullable ComponentName launcherComponent,
-            @Nullable String appPredictionServicePackage) {
+    void sendSessionCommitBroadcast(@NonNull Computer snapshot,
+                                    @NonNull PackageInstaller.SessionInfo sessionInfo,
+                                    int userId,
+                                    @Nullable String appPredictionServicePackage) {
+        UserManagerService ums = UserManagerService.getInstance();
+        if (ums == null || sessionInfo.isStaged()) {
+            return;
+        }
+        final UserInfo parent = ums.getProfileParent(userId);
+        final int launcherUserId = (parent != null) ? parent.id : userId;
+        final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId);
         if (launcherComponent != null) {
             Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
                     .setPackage(launcherComponent.getPackageName());
-            mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+            mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId));
         }
         // TODO(b/122900055) Change/Remove this and replace with new permission role.
         if (appPredictionServicePackage != null) {
@@ -301,30 +347,278 @@
                     .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
                     .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
                     .setPackage(appPredictionServicePackage);
-            mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
+            mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUserId));
         }
     }
 
-    public void sendPreferredActivityChangedBroadcast(int userId) {
-        final IActivityManager am = ActivityManager.getService();
-        if (am == null) {
+    void sendPreferredActivityChangedBroadcast(int userId) {
+        mHandler.post(() -> {
+            final IActivityManager am = ActivityManager.getService();
+            if (am == null) {
+                return;
+            }
+
+            final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            try {
+                am.broadcastIntentWithFeature(null, null, intent, null, null,
+                        0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
+                        null, false, false, userId);
+            } catch (RemoteException e) {
+            }
+        });
+    }
+
+    void sendPostInstallBroadcasts(@NonNull Computer snapshot,
+                                   @NonNull InstallRequest request,
+                                   @NonNull String packageName,
+                                   @NonNull String requiredPermissionControllerPackage,
+                                   @NonNull String[] requiredVerifierPackages,
+                                   @NonNull String requiredInstallerPackage,
+                                   @NonNull PackageSender packageSender,
+                                   boolean isLaunchedForRestore,
+                                   boolean isKillApp,
+                                   boolean isUpdate,
+                                   boolean isArchived) {
+        // Send the removed broadcasts
+        if (request.getRemovedInfo() != null) {
+            if (request.getRemovedInfo().mIsExternal) {
+                if (DEBUG_INSTALL) {
+                    Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
+                            + " is ASEC-hosted -> UNAVAILABLE");
+                }
+                final String[] pkgNames = new String[]{
+                        request.getRemovedInfo().mRemovedPackage};
+                final int[] uids = new int[]{request.getRemovedInfo().mUid};
+                notifyResourcesChanged(
+                        false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+                sendResourcesChangedBroadcast(
+                        snapshot, false /* mediaStatus */, true /* replacing */, pkgNames, uids);
+            }
+            sendPackageRemovedBroadcasts(
+                    request.getRemovedInfo(), packageSender, isKillApp, false /*removedBySystem*/,
+                    false /*isArchived*/);
+        }
+
+        final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
+        final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
+        final int[] updateUserIds = request.getUpdateBroadcastUserIds();
+        final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
+
+        final String installerPackageName =
+                request.getInstallerPackageName() != null
+                        ? request.getInstallerPackageName()
+                        : request.getRemovedInfo() != null
+                        ? request.getRemovedInfo().mInstallerPackageName
+                        : null;
+
+        Bundle extras = new Bundle();
+        extras.putInt(Intent.EXTRA_UID, request.getAppId());
+        if (isUpdate) {
+            extras.putBoolean(Intent.EXTRA_REPLACING, true);
+        }
+        if (isArchived) {
+            extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+        }
+        extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, request.getDataLoaderType());
+
+        final String staticSharedLibraryName = request.getPkg().getStaticSharedLibraryName();
+        // If a package is a static shared library, then only the installer of the package
+        // should get the broadcast.
+        if (installerPackageName != null && staticSharedLibraryName != null) {
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+                    extras, 0 /*flags*/,
+                    installerPackageName, null /*finishedReceiver*/,
+                    request.getNewUsers(), null /* instantUserIds*/,
+                    null /* broadcastAllowList */, null);
+        }
+
+        // Send installed broadcasts if the package is not a static shared lib.
+        if (staticSharedLibraryName == null) {
+            // Send PACKAGE_ADDED broadcast for users that see the package for the first time
+            // sendPackageAddedForNewUsers also deals with system apps
+            final int appId = UserHandle.getAppId(request.getAppId());
+            final boolean isSystem = request.isInstallSystem();
+            final boolean isVirtualPreload =
+                    ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+            sendPackageAddedForNewUsers(snapshot, packageName,
+                    isSystem || isVirtualPreload,
+                    isVirtualPreload /*startReceiver*/, appId,
+                    firstUserIds, firstInstantUserIds, isArchived, request.getDataLoaderType());
+
+            // Send PACKAGE_ADDED broadcast for users that don't see
+            // the package for the first time
+
+            // Send to all running apps.
+            final SparseArray<int[]> newBroadcastAllowList =
+                    mAppsFilter.getVisibilityAllowList(snapshot,
+                            snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+                            updateUserIds, snapshot.getPackageStates());
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+                    extras, 0 /*flags*/,
+                    null /*targetPackage*/, null /*finishedReceiver*/,
+                    updateUserIds, instantUserIds, newBroadcastAllowList, null);
+            // Send to the installer, even if it's not running.
+            if (installerPackageName != null) {
+                sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+                        extras, 0 /*flags*/,
+                        installerPackageName, null /*finishedReceiver*/,
+                        updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+            }
+            // Send to PermissionController for all update users, even if it may not be running
+            // for some users
+            if (isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
+                sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+                        extras, 0 /*flags*/,
+                        requiredPermissionControllerPackage, null /*finishedReceiver*/,
+                        updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
+            }
+            // Notify required verifier(s) that are not the installer of record for the package.
+            for (String verifierPackageName : requiredVerifierPackages) {
+                if (verifierPackageName != null && !verifierPackageName.equals(
+                        installerPackageName)) {
+                    sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+                            packageName,
+                            extras, 0 /*flags*/,
+                            verifierPackageName, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds, null /* broadcastAllowList */,
+                            null);
+                }
+            }
+            // If package installer is defined, notify package installer about new
+            // app installed
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, packageName,
+                    extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+                    requiredInstallerPackage, null /*finishedReceiver*/,
+                    firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
+
+            // Send replaced for users that don't see the package for the first time
+            if (isUpdate) {
+                sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+                        packageName, extras, 0 /*flags*/,
+                        null /*targetPackage*/, null /*finishedReceiver*/,
+                        updateUserIds, instantUserIds,
+                        request.getRemovedInfo().mBroadcastAllowList, null);
+                if (installerPackageName != null) {
+                    sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, packageName,
+                            extras, 0 /*flags*/,
+                            installerPackageName, null /*finishedReceiver*/,
+                            updateUserIds, instantUserIds, null /*broadcastAllowList*/,
+                            null);
+                }
+                for (String verifierPackageName : requiredVerifierPackages) {
+                    if (verifierPackageName != null && !verifierPackageName.equals(
+                            installerPackageName)) {
+                        sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+                                packageName, extras, 0 /*flags*/, verifierPackageName,
+                                null /*finishedReceiver*/, updateUserIds, instantUserIds,
+                                null /*broadcastAllowList*/, null);
+                    }
+                }
+                sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED,
+                        null /*package*/, null /*extras*/, 0 /*flags*/,
+                        packageName /*targetPackage*/,
+                        null /*finishedReceiver*/, updateUserIds, instantUserIds,
+                        null /*broadcastAllowList*/,
+                        getTemporaryAppAllowlistBroadcastOptions(
+                                REASON_PACKAGE_REPLACED).toBundle());
+            } else if (isLaunchedForRestore && !request.isInstallSystem()) {
+                // First-install and we did a restore, so we're responsible for the
+                // first-launch broadcast.
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "Post-restore of " + packageName
+                            + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
+                }
+                sendFirstLaunchBroadcast(packageName, installerPackageName,
+                        firstUserIds, firstInstantUserIds);
+            }
+
+            // Send broadcast package appeared if external for all users
+            if (request.getPkg().isExternalStorage()) {
+                if (!isUpdate) {
+                    final StorageManager storage = mContext.getSystemService(StorageManager.class);
+                    VolumeInfo volume =
+                            storage.findVolumeByUuid(
+                                    StorageManager.convert(
+                                            request.getPkg().getVolumeUuid()).toString());
+                    int packageExternalStorageType =
+                            PackageManagerServiceUtils.getPackageExternalStorageType(volume,
+                                    /* isExternalStorage */ true);
+                    // If the package was installed externally, log it.
+                    if (packageExternalStorageType != StorageEnums.UNKNOWN) {
+                        FrameworkStatsLog.write(
+                                FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+                                packageExternalStorageType, packageName);
+                    }
+                }
+                if (DEBUG_INSTALL) {
+                    Slog.i(TAG, "upgrading pkg " + packageName + " is external");
+                }
+                if (!isArchived) {
+                    final String[] pkgNames = new String[]{packageName};
+                    final int[] uids = new int[]{request.getPkg().getUid()};
+                    sendResourcesChangedBroadcast(snapshot,
+                            true /* mediaStatus */, true /* replacing */, pkgNames, uids);
+                    notifyResourcesChanged(true /* mediaStatus */,
+                            true /* replacing */, pkgNames, uids);
+                }
+            }
+        } else { // if static shared lib
+            final ArrayList<AndroidPackage> libraryConsumers = request.getLibraryConsumers();
+            if (!ArrayUtils.isEmpty(libraryConsumers)) {
+                // No need to kill consumers if it's installation of new version static shared lib.
+                final boolean dontKillApp = !isUpdate;
+                for (int i = 0; i < libraryConsumers.size(); i++) {
+                    AndroidPackage pkg = libraryConsumers.get(i);
+                    // send broadcast that all consumers of the static shared library have changed
+                    sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+                            dontKillApp,
+                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
+                            pkg.getUid(), null);
+                }
+            }
+        }
+    }
+
+    private void sendPackageAddedForNewUsers(@NonNull Computer snapshot,
+                                             @NonNull String packageName,
+                                             boolean sendBootCompleted,
+                                             boolean includeStopped,
+                                             @AppIdInt int appId,
+                                             int[] userIds,
+                                             int[] instantUserIds,
+                                             boolean isArchived,
+                                             int dataLoaderType) {
+        if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
             return;
         }
-
-        final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
-        intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        try {
-            am.broadcastIntentWithFeature(null, null, intent, null, null,
-                    0, null, null, null, null, null, android.app.AppOpsManager.OP_NONE,
-                    null, false, false, userId);
-        } catch (RemoteException e) {
+        SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
+                snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
+                userIds, snapshot.getPackageStates());
+        mHandler.post(
+                () -> sendPackageAddedForNewUsers(packageName, appId, userIds,
+                        instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
+        mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
+                instantUserIds, isArchived, dataLoaderType, broadcastAllowList, mHandler);
+        if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
+            mHandler.post(() -> {
+                        for (int userId : userIds) {
+                            sendBootCompletedBroadcastToSystemApp(
+                                    packageName, includeStopped, userId);
+                        }
+                    }
+            );
         }
     }
 
-    public void sendPackageAddedForNewUsers(String packageName, @AppIdInt int appId, int[] userIds,
-            int[] instantUserIds, boolean isArchived, int dataLoaderType,
-            SparseArray<int[]> broadcastAllowlist) {
+    private void sendPackageAddedForNewUsers(@NonNull String packageName,
+                                             @AppIdInt int appId,
+                                             int[] userIds,
+                                             int[] instantUserIds,
+                                             boolean isArchived,
+                                             int dataLoaderType,
+                                             @NonNull SparseArray<int[]> broadcastAllowlist) {
         Bundle extras = new Bundle(1);
         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
         final int uid = UserHandle.getUid(
@@ -349,7 +643,30 @@
         }
     }
 
-    public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+    void sendPackageAddedForUser(@NonNull Computer snapshot,
+                                 @NonNull String packageName,
+                                 @NonNull PackageStateInternal packageState,
+                                 int userId,
+                                 boolean isArchived,
+                                 int dataLoaderType,
+                                 @Nullable String appPredictionServicePackage) {
+        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
+        final boolean isSystem = packageState.isSystem();
+        final boolean isInstantApp = userState.isInstantApp();
+        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+        sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
+                false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
+                isArchived, dataLoaderType);
+
+        // Send a session commit broadcast
+        final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
+        info.installReason = userState.getInstallReason();
+        info.appPackageName = packageName;
+        sendSessionCommitBroadcast(snapshot, info, userId, appPredictionServicePackage);
+    }
+
+    void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
             int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
                 installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */,
@@ -366,7 +683,7 @@
      * access all the packages in the extras.
      */
     @Nullable
-    public static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
+    private static Bundle filterExtrasChangedPackageList(@NonNull Computer snapshot, int callingUid,
             @NonNull Bundle extras) {
         if (UserHandle.isCore(callingUid)) {
             // see all
@@ -392,7 +709,7 @@
     }
 
     /** Returns whether the Safety Label Change notification, a privacy feature, is enabled. */
-    public static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
+    private static boolean isPrivacySafetyLabelChangeNotificationsEnabled(Context context) {
         PackageManager packageManager = context.getPackageManager();
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
                 SAFETY_LABEL_CHANGE_NOTIFICATIONS_ENABLED, true)
@@ -424,4 +741,323 @@
                 pkgList.size() > 0 ? pkgList.toArray(new String[pkgList.size()]) : null,
                 uidList != null && uidList.size() > 0 ? uidList.toArray() : null);
     }
+
+    void sendApplicationHiddenForUser(@NonNull String packageName,
+                                      @NonNull PackageStateInternal packageState,
+                                      int userId,
+                                      @NonNull PackageSender packageSender) {
+        final PackageRemovedInfo info = new PackageRemovedInfo();
+        info.mRemovedPackage = packageName;
+        info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
+        info.mRemovedUsers = new int[] {userId};
+        info.mBroadcastUsers = new int[] {userId};
+        info.mUid = UserHandle.getUid(userId, packageState.getAppId());
+        info.mRemovedPackageVersionCode = packageState.getVersionCode();
+        sendPackageRemovedBroadcasts(info, packageSender, true /*killApp*/,
+                false /*removedBySystem*/, false /*isArchived*/);
+    }
+
+    void sendPackageChangedBroadcast(@NonNull Computer snapshot,
+                                     @NonNull String packageName,
+                                     boolean dontKillApp,
+                                     @NonNull ArrayList<String> componentNames,
+                                     int packageUid,
+                                     @NonNull String reason) {
+        PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
+                Process.SYSTEM_UID);
+        if (setting == null) {
+            return;
+        }
+        final int userId = UserHandle.getUserId(packageUid);
+        final boolean isInstantApp =
+                snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
+        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
+        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+        final SparseArray<int[]> broadcastAllowList =
+                isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
+        mHandler.post(() -> sendPackageChangedBroadcast(
+                packageName, dontKillApp, componentNames, packageUid, reason, userIds,
+                instantUserIds, broadcastAllowList));
+        mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
+                packageUid, reason, userIds, instantUserIds, broadcastAllowList, mHandler);
+    }
+
+    private void sendPackageBroadcastAndNotify(@NonNull String action,
+                                               @NonNull  String pkg,
+                                               @NonNull  Bundle extras,
+                                               int flags,
+                                               @Nullable String targetPkg,
+                                               @Nullable IIntentReceiver finishedReceiver,
+                                               @NonNull int[] userIds,
+                                               @NonNull int[] instantUserIds,
+                                               @Nullable SparseArray<int[]> broadcastAllowList,
+                                               @Nullable Bundle bOptions) {
+        mHandler.post(() -> sendPackageBroadcast(action, pkg, extras, flags,
+                targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
+                null /* filterExtrasForReceiver */, bOptions));
+        if (targetPkg == null) {
+            // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
+            // many times to different targets, e.g. installer app, permission controller, other
+            // registered apps. We should filter it to avoid calling back many times for the same
+            // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
+            // installer app or null for registered apps. The callback only need to send back to the
+            // registered apps so we check the null condition here.
+            notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
+        }
+    }
+
+    void sendSystemPackageUpdatedBroadcasts(@NonNull PackageRemovedInfo packageRemovedInfo) {
+        if (!packageRemovedInfo.mIsRemovedPackageSystemUpdate) {
+            return;
+        }
+
+        final String removedPackage = packageRemovedInfo.mRemovedPackage;
+        final int removedAppId = packageRemovedInfo.mRemovedAppId;
+        final int uid = packageRemovedInfo.mUid;
+        final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+        final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+
+        Bundle extras = new Bundle(2);
+        extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
+        extras.putBoolean(Intent.EXTRA_REPLACING, true);
+        sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
+                0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+
+        if (installerPackageName != null) {
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_ADDED,
+                    removedPackage, extras, 0 /*flags*/,
+                    installerPackageName, null, null, null, null /* broadcastAllowList */,
+                    null);
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED,
+                    removedPackage, extras, 0 /*flags*/,
+                    installerPackageName, null, null, null, null /* broadcastAllowList */,
+                    null);
+        }
+        sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
+                extras, 0, null /*targetPackage*/, null, null, null, broadcastAllowList, null);
+        sendPackageBroadcastAndNotify(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+                removedPackage, null, null, null, null /* broadcastAllowList */,
+                getTemporaryBroadcastOptionsForSystemPackageUpdate(REASON_PACKAGE_REPLACED)
+                        .toBundle());
+    }
+
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private @NonNull BroadcastOptions getTemporaryBroadcastOptionsForSystemPackageUpdate(
+            @PowerExemptionManager.ReasonCode int reasonCode) {
+        long duration = 10_000;
+        if (mAmInternal != null) {
+            duration = mAmInternal.getBootTimeTempAllowListDuration();
+        }
+        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+        bOptions.setTemporaryAppAllowlist(duration,
+                TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                reasonCode, "");
+        return bOptions;
+    }
+
+
+    void sendPackageRemovedBroadcasts(
+            @NonNull PackageRemovedInfo packageRemovedInfo,
+            @NonNull PackageSender packageSender,
+            boolean killApp,
+            boolean removedBySystem,
+            boolean isArchived) {
+        final String removedPackage = packageRemovedInfo.mRemovedPackage;
+        final int removedAppId = packageRemovedInfo.mRemovedAppId;
+        final int uid = packageRemovedInfo.mUid;
+        final String installerPackageName = packageRemovedInfo.mInstallerPackageName;
+        final int[] broadcastUserIds = packageRemovedInfo.mBroadcastUsers;
+        final int[] instantUserIds = packageRemovedInfo.mInstantUserIds;
+        final SparseArray<int[]> broadcastAllowList = packageRemovedInfo.mBroadcastAllowList;
+        final boolean dataRemoved = packageRemovedInfo.mDataRemoved;
+        final boolean isUpdate = packageRemovedInfo.mIsUpdate;
+        final boolean isRemovedPackageSystemUpdate =
+                packageRemovedInfo.mIsRemovedPackageSystemUpdate;
+        final boolean isRemovedForAllUsers = packageRemovedInfo.mRemovedForAllUsers;
+        final boolean isStaticSharedLib = packageRemovedInfo.mIsStaticSharedLib;
+
+        Bundle extras = new Bundle();
+        final int removedUid = removedAppId >= 0  ? removedAppId : uid;
+        extras.putInt(Intent.EXTRA_UID, removedUid);
+        extras.putBoolean(Intent.EXTRA_DATA_REMOVED, dataRemoved);
+        extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, isRemovedPackageSystemUpdate);
+        extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
+        extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
+        final boolean isReplace = isUpdate || isRemovedPackageSystemUpdate;
+        if (isReplace || isArchived) {
+            extras.putBoolean(Intent.EXTRA_REPLACING, true);
+        }
+        if (isArchived) {
+            extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+        }
+        extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, isRemovedForAllUsers);
+
+        // Send PACKAGE_REMOVED broadcast to the respective installer.
+        if (removedPackage != null && installerPackageName != null) {
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+                    removedPackage, extras, 0 /*flags*/,
+                    installerPackageName, null, broadcastUserIds, instantUserIds, null, null);
+        }
+        if (isStaticSharedLib) {
+            // When uninstalling static shared libraries, only the package's installer needs to be
+            // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
+            return;
+        }
+        if (removedPackage != null) {
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED,
+                    removedPackage, extras, 0, null /*targetPackage*/, null,
+                    broadcastUserIds, instantUserIds, broadcastAllowList, null);
+            sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+                    removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+                    null /*finishedReceiver*/, broadcastUserIds, instantUserIds,
+                    broadcastAllowList, null /*bOptions*/);
+            if (dataRemoved && !isRemovedPackageSystemUpdate) {
+                sendPackageBroadcastAndNotify(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+                        removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
+                        null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+                packageSender.notifyPackageRemoved(removedPackage, removedUid);
+            }
+        }
+        if (removedAppId >= 0) {
+            // If a system app's updates are uninstalled the UID is not actually removed. Some
+            // services need to know the package name affected.
+            if (isReplace) {
+                extras.putString(Intent.EXTRA_PACKAGE_NAME, removedPackage);
+            }
+
+            sendPackageBroadcastAndNotify(Intent.ACTION_UID_REMOVED,
+                    null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+                    null, null, broadcastUserIds, instantUserIds, broadcastAllowList, null);
+        }
+    }
+
+    /**
+     * Send broadcast intents for packages suspension changes.
+     *
+     * @param intent The action name of the suspension intent.
+     * @param pkgList The names of packages which have suspension changes.
+     * @param uidList The uids of packages which have suspension changes.
+     * @param userId The user where packages reside.
+     */
+    void sendPackagesSuspendedOrUnsuspendedForUser(@NonNull Computer snapshot,
+                                                   @NonNull String intent,
+                                                   @NonNull String[] pkgList,
+                                                   @NonNull int[] uidList,
+                                                   boolean quarantined,
+                                                   int userId) {
+        final Bundle extras = new Bundle(3);
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        if (quarantined) {
+            extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
+        }
+        final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
+        final Bundle options = new BroadcastOptions()
+                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
+                .toBundle();
+        mHandler.post(() -> sendPackageBroadcast(intent, null /* pkg */,
+                extras, flags, null /* targetPkg */, null /* finishedReceiver */,
+                new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
+                (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
+                        snapshot, callingUid, intentExtras),
+                options));
+        notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
+                null /* instantUserIds */, null /* broadcastAllowList */);
+    }
+
+    void sendMyPackageSuspendedOrUnsuspended(@NonNull Computer snapshot,
+                                             @NonNull String[] affectedPackages,
+                                             boolean suspended,
+                                             int userId) {
+        final String action = suspended
+                ? Intent.ACTION_MY_PACKAGE_SUSPENDED
+                : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+        mHandler.post(() -> {
+            final IActivityManager am = ActivityManager.getService();
+            if (am == null) {
+                Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                        + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                return;
+            }
+            final int[] targetUserIds = new int[] {userId};
+            for (String packageName : affectedPackages) {
+                final Bundle appExtras = suspended
+                        ? SuspendPackageHelper.getSuspendedPackageAppExtras(
+                                snapshot, packageName, userId, SYSTEM_UID)
+                        : null;
+                final Bundle intentExtras;
+                if (appExtras != null) {
+                    intentExtras = new Bundle(1);
+                    intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+                } else {
+                    intentExtras = null;
+                }
+                doSendBroadcast(action, null, intentExtras,
+                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                        targetUserIds, false, null, null, null);
+            }
+        });
+    }
+
+    /**
+     * Send broadcast intents for packages distracting changes.
+     *
+     * @param pkgList The names of packages which have suspension changes.
+     * @param uidList The uids of packages which have suspension changes.
+     * @param userId The user where packages reside.
+     */
+    void sendDistractingPackagesChanged(@NonNull Computer snapshot,
+                                        @NonNull String[] pkgList,
+                                        @NonNull int[] uidList,
+                                        int userId,
+                                        int distractionFlags) {
+        final Bundle extras = new Bundle();
+        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+
+        mHandler.post(() -> sendPackageBroadcast(
+                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
+                extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
+                null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
+                null /* broadcastAllowList */,
+                (callingUid, intentExtras) -> filterExtrasChangedPackageList(
+                        snapshot, callingUid, intentExtras),
+                null /* bOptions */));
+    }
+
+    void sendResourcesChangedBroadcastAndNotify(@NonNull Computer snapshot,
+                                                boolean mediaStatus,
+                                                boolean replacing,
+                                                @NonNull ArrayList<AndroidPackage> packages) {
+        final int size = packages.size();
+        final String[] packageNames = new String[size];
+        final int[] packageUids = new int[size];
+        for (int i = 0; i < size; i++) {
+            final AndroidPackage pkg = packages.get(i);
+            packageNames[i] = pkg.getPackageName();
+            packageUids[i] = pkg.getUid();
+        }
+        sendResourcesChangedBroadcast(snapshot, mediaStatus,
+                replacing, packageNames, packageUids);
+        notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
+    }
+
+    private void notifyPackageMonitor(@NonNull String action,
+                                      @NonNull String pkg,
+                                      @Nullable Bundle extras,
+                                      @NonNull int[] userIds,
+                                      @NonNull int[] instantUserIds,
+                                      @Nullable SparseArray<int[]> broadcastAllowList) {
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
+                instantUserIds, broadcastAllowList, mHandler);
+    }
+
+    private void notifyResourcesChanged(boolean mediaStatus,
+                                boolean replacing,
+                                @NonNull String[] pkgNames,
+                                @NonNull int[] uids) {
+        mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
+                uids, mHandler);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 83f90a1..8e767e7 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -63,7 +63,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
-import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.ArchiveState;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -87,19 +86,16 @@
 
     private final PackageManagerService mPm;
     private final UserManagerInternal mUserManagerInternal;
-    private final PermissionManagerServiceInternal mPermissionManager;
     private final RemovePackageHelper mRemovePackageHelper;
+    private final BroadcastHelper mBroadcastHelper;
 
     // TODO(b/198166813): remove PMS dependency
-    DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) {
+    DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
+                        BroadcastHelper broadcastHelper) {
         mPm = pm;
         mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
-        mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
         mRemovePackageHelper = removePackageHelper;
-    }
-
-    DeletePackageHelper(PackageManagerService pm) {
-        this(pm, new RemovePackageHelper(pm));
+        mBroadcastHelper = broadcastHelper;
     }
 
     /**
@@ -121,7 +117,7 @@
      */
     public int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
             boolean removedBySystem) {
-        final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+        final PackageRemovedInfo info = new PackageRemovedInfo();
         final boolean res;
 
         final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
@@ -251,8 +247,9 @@
         if (res) {
             final boolean killApp = (deleteFlags & PackageManager.DELETE_DONT_KILL_APP) == 0;
             final boolean isArchived = (deleteFlags & PackageManager.DELETE_ARCHIVE) != 0;
-            info.sendPackageRemovedBroadcasts(killApp, removedBySystem, isArchived);
-            info.sendSystemPackageUpdatedBroadcasts();
+            mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm, killApp,
+                    removedBySystem, isArchived);
+            mBroadcastHelper.sendSystemPackageUpdatedBroadcasts(info);
             PackageMetrics.onUninstallSucceeded(info, deleteFlags, removeUser);
         }
 
@@ -314,7 +311,7 @@
                             Slog.i(TAG, "Enabling system stub after removal; pkg: "
                                     + stubPkg.getPackageName());
                         }
-                        new InstallPackageHelper(mPm).enableCompressedPackage(stubPkg, stubPs);
+                        mPm.enableCompressedPackage(stubPkg, stubPs);
                     } else if (DEBUG_COMPRESSION) {
                         Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
                                 + "after removal; pkg: " + stubPkg.getPackageName());
@@ -491,8 +488,7 @@
             // When an updated system application is deleted we delete the existing resources
             // as well and fall back to existing code in system partition
             deleteInstalledSystemPackage(action, allUserHandles, writeSettings);
-            new InstallPackageHelper(mPm).restoreDisabledSystemPackageLIF(
-                    action, allUserHandles, writeSettings);
+            mPm.restoreDisabledSystemPackageLIF(action, allUserHandles, writeSettings);
         } else {
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
             if (ps.isIncremental()) {
diff --git a/services/core/java/com/android/server/pm/DistractingPackageHelper.java b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
index 8ebb6ea..c5ec73b 100644
--- a/services/core/java/com/android/server/pm/DistractingPackageHelper.java
+++ b/services/core/java/com/android/server/pm/DistractingPackageHelper.java
@@ -19,10 +19,7 @@
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 
 import android.annotation.NonNull;
-import android.content.Intent;
 import android.content.pm.PackageManager.DistractionRestriction;
-import android.os.Bundle;
-import android.os.Handler;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.IntArray;
@@ -42,17 +39,16 @@
 
     // TODO(b/198166813): remove PMS dependency
     private final PackageManagerService mPm;
-    private final PackageManagerServiceInjector mInjector;
     private final BroadcastHelper mBroadcastHelper;
     private final SuspendPackageHelper mSuspendPackageHelper;
 
     /**
      * Constructor for {@link PackageManagerService}.
      */
-    DistractingPackageHelper(PackageManagerService pm, PackageManagerServiceInjector injector,
-            BroadcastHelper broadcastHelper, SuspendPackageHelper suspendPackageHelper) {
+    DistractingPackageHelper(PackageManagerService pm,
+                             BroadcastHelper broadcastHelper,
+                             SuspendPackageHelper suspendPackageHelper) {
         mPm = pm;
-        mInjector = injector;
         mBroadcastHelper = broadcastHelper;
         mSuspendPackageHelper = suspendPackageHelper;
     }
@@ -127,8 +123,8 @@
         if (!changedPackagesList.isEmpty()) {
             final String[] changedPackages = changedPackagesList.toArray(
                     new String[changedPackagesList.size()]);
-            sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
-                    restrictionFlags);
+            mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+                    changedPackages, changedUids.toArray(), userId, restrictionFlags);
             mPm.scheduleWritePackageRestrictions(userId);
         }
         return unactionedPackages.toArray(new String[0]);
@@ -202,34 +198,9 @@
         if (!changedPackages.isEmpty()) {
             final String[] packageArray = changedPackages.toArray(
                     new String[changedPackages.size()]);
-            sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId,
-                    RESTRICTION_NONE);
+            mBroadcastHelper.sendDistractingPackagesChanged(mPm.snapshotComputer(),
+                    packageArray, changedUids.toArray(), userId, RESTRICTION_NONE);
             mPm.scheduleWritePackageRestrictions(userId);
         }
     }
-
-    /**
-     * Send broadcast intents for packages distracting changes.
-     *
-     * @param pkgList The names of packages which have suspension changes.
-     * @param uidList The uids of packages which have suspension changes.
-     * @param userId The user where packages reside.
-     */
-    void sendDistractingPackagesChanged(@NonNull String[] pkgList, int[] uidList, int userId,
-            int distractionFlags) {
-        final Bundle extras = new Bundle();
-        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
-        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
-        extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
-
-        final Handler handler = mInjector.getHandler();
-        handler.post(() -> mBroadcastHelper.sendPackageBroadcast(
-                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null /* pkg */,
-                extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null /* targetPkg */,
-                null /* finishedReceiver */, new int[]{userId}, null /* instantUserIds */,
-                null /* broadcastAllowList */,
-                (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
-                        mPm.snapshotComputer(), callingUid, intentExtras),
-                null /* bOptions */));
-    }
 }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 486b1ad..8f71a9b 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -38,7 +38,6 @@
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -50,7 +49,6 @@
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
-import static com.android.server.pm.PackageManagerService.DEBUG_BACKUP;
 import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -130,7 +128,6 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Message;
@@ -143,9 +140,6 @@
 import android.os.UserManager;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.stats.storage.StorageEnums;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -163,7 +157,6 @@
 import com.android.internal.security.VerityUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.SystemConfig;
@@ -220,6 +213,7 @@
     private final AppDataHelper mAppDataHelper;
     private final BroadcastHelper mBroadcastHelper;
     private final RemovePackageHelper mRemovePackageHelper;
+    private final DeletePackageHelper mDeletePackageHelper;
     private final IncrementalManager mIncrementalManager;
     private final ApexManager mApexManager;
     private final DexManager mDexManager;
@@ -233,12 +227,17 @@
     private final UpdateOwnershipHelper mUpdateOwnershipHelper;
 
     // TODO(b/198166813): remove PMS dependency
-    InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+    InstallPackageHelper(PackageManagerService pm,
+                         AppDataHelper appDataHelper,
+                         RemovePackageHelper removePackageHelper,
+                         DeletePackageHelper deletePackageHelper,
+                         BroadcastHelper broadcastHelper) {
         mPm = pm;
         mInjector = pm.mInjector;
         mAppDataHelper = appDataHelper;
-        mBroadcastHelper = new BroadcastHelper(pm.mInjector);
-        mRemovePackageHelper = new RemovePackageHelper(pm);
+        mBroadcastHelper = broadcastHelper;
+        mRemovePackageHelper = removePackageHelper;
+        mDeletePackageHelper = deletePackageHelper;
         mIncrementalManager = pm.mInjector.getIncrementalManager();
         mApexManager = pm.mInjector.getApexManager();
         mDexManager = pm.mInjector.getDexManager();
@@ -251,10 +250,6 @@
         mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
     }
 
-    InstallPackageHelper(PackageManagerService pm) {
-        this(pm, new AppDataHelper(pm));
-    }
-
     /**
      * Commits the package scan and modifies system state.
      * <p><em>WARNING:</em> The method may throw an exception in the middle
@@ -263,7 +258,7 @@
      * possible and the system is not left in an inconsistent state.
      */
     @GuardedBy("mPm.mLock")
-    public AndroidPackage commitReconciledScanResultLocked(
+    private AndroidPackage commitReconciledScanResultLocked(
             @NonNull ReconciledPackage reconciledPkg, int[] allUsers) {
         final InstallRequest request = reconciledPkg.mInstallRequest;
         // TODO(b/135203078): Move this even further away
@@ -731,8 +726,9 @@
                 }
                 // TODO(b/278553670) Store archive state for the user.
                 boolean isArchived = (pkgSetting.getPkg() == null);
-                mPm.sendPackageAddedForUser(mPm.snapshotComputer(), packageName, pkgSetting, userId,
-                        isArchived, DataLoaderType.NONE);
+                mBroadcastHelper.sendPackageAddedForUser(mPm.snapshotComputer(), packageName,
+                        pkgSetting, userId, isArchived, DataLoaderType.NONE,
+                        mPm.mAppPredictionServicePackage);
                 synchronized (mPm.mLock) {
                     mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
                 }
@@ -998,8 +994,7 @@
                         return;
                     }
                     final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
-                    final boolean isSdkLibrary = packageToScan.isSdkLibrary();
-                    if (!isApex && !isSdkLibrary) {
+                    if (!isApex) {
                         createdAppId.put(packageName, optimisticallyRegisterAppId(request));
                     } else {
                         request.getScannedPackageSetting().setAppId(Process.INVALID_UID);
@@ -1746,7 +1741,7 @@
                 }
 
                 // Update what is removed
-                PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm);
+                PackageRemovedInfo removedInfo = new PackageRemovedInfo();
                 removedInfo.mUid = ps.getAppId();
                 removedInfo.mRemovedPackage = ps.getPackageName();
                 removedInfo.mInstallerPackageName =
@@ -2075,8 +2070,6 @@
             final InstallRequest installRequest = reconciledPkg.mInstallRequest;
             final ParsedPackage parsedPackage = installRequest.getParsedPackage();
             final String packageName = parsedPackage.getPackageName();
-            final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
-            final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
 
             installRequest.onCommitStarted();
             if (installRequest.isInstallReplace()) {
@@ -2098,7 +2091,7 @@
                                 allUsers, mPm.mSettings.getPackagesLocked());
                 if (installRequest.isInstallSystem()) {
                     // Remove existing system package
-                    removePackageHelper.removePackage(oldPackage, true);
+                    mRemovePackageHelper.removePackage(oldPackage, true);
                     if (!disableSystemPackageLPw(oldPackage)) {
                         // We didn't need to disable the .apk as a current system package,
                         // which means we are replacing another update that is already
@@ -2114,7 +2107,7 @@
                 } else {
                     try {
                         // Settings will be written during the call to updateSettingsLI().
-                        deletePackageHelper.executeDeletePackage(
+                        mDeletePackageHelper.executeDeletePackage(
                                 reconciledPkg.mDeletePackageAction, packageName,
                                 true, allUsers, false);
                     } catch (SystemDeleteException e) {
@@ -2201,12 +2194,19 @@
         final String installerPackageName = installRequest.getInstallerPackageName();
 
         if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
+        final int userId = installRequest.getUserId();
+        if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT
+                && !mPm.mUserManager.exists(userId)) {
+            installRequest.setError(PackageManagerException.ofInternalError(
+                    "User " + userId + " doesn't exist or has been removed",
+                    PackageManagerException.INTERNAL_ERROR_MISSING_USER));
+            return;
+        }
         synchronized (mPm.mLock) {
             // For system-bundled packages, we assume that installing an upgraded version
             // of the package implies that the user actually wants to run that new code,
             // so we enable the package.
             final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
-            final int userId = installRequest.getUserId();
             if (ps != null) {
                 if (ps.isSystem()) {
                     if (DEBUG_INSTALL) {
@@ -2797,24 +2797,21 @@
         final Computer snapshot = mPm.snapshotComputer();
         // Send broadcasts
         for (int i = 0; i < numBroadcasts; i++) {
-            mPm.sendPackageChangedBroadcast(snapshot, packages[i], true /* dontKillApp */,
-                    components[i], uids[i], null /* reason */);
+            mBroadcastHelper.sendPackageChangedBroadcast(snapshot, packages[i],
+                    true /* dontKillApp */, components[i], uids[i], null /* reason */);
         }
     }
 
     void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) {
         final boolean killApp =
                 (request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0;
-        final boolean virtualPreload =
-                ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-        final String installerPackage = request.getInstallerPackageName();
-        final int dataLoaderType = request.getDataLoaderType();
         final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED;
         final boolean update = request.isUpdate();
         final boolean archived = request.isArchived();
         final String packageName = request.getName();
+        final Computer snapshot = mPm.snapshotComputer();
         final PackageStateInternal pkgSetting =
-                succeeded ? mPm.snapshotComputer().getPackageStateInternal(packageName) : null;
+                succeeded ? snapshot.getPackageStateInternal(packageName) : null;
         final boolean removedBeforeUpdate = (pkgSetting == null)
                 || (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
                 request.getPkg().getPath()));
@@ -2835,208 +2832,22 @@
             // Clear the uid cache after we installed a new package.
             mPm.mPerUidReadTimeoutsCache = null;
 
-            // Send the removed broadcasts
-            if (request.getRemovedInfo() != null) {
-                if (request.getRemovedInfo().mIsExternal) {
-                    if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
-                                + " is ASEC-hosted -> UNAVAILABLE");
-                    }
-                    final String[] pkgNames = new String[]{
-                            request.getRemovedInfo().mRemovedPackage};
-                    final int[] uids = new int[]{request.getRemovedInfo().mUid};
-                    mPm.notifyResourcesChanged(false /* mediaStatus */,
-                            true /* replacing */, pkgNames, uids);
-                    mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
-                            false /* mediaStatus */, true /* replacing */, pkgNames, uids);
-                }
-                request.getRemovedInfo().sendPackageRemovedBroadcasts(
-                        killApp, false /*removedBySystem*/, false /*isArchived*/);
-            }
-
-            final String installerPackageName =
-                    request.getInstallerPackageName() != null
-                            ? request.getInstallerPackageName()
-                            : request.getRemovedInfo() != null
-                                    ? request.getRemovedInfo().mInstallerPackageName
-                                    : null;
-
             mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(),
                     request.getNewUsers());
 
             request.populateBroadcastUsers();
             final int[] firstUserIds = request.getFirstTimeBroadcastUserIds();
-            final int[] firstInstantUserIds = request.getFirstTimeBroadcastInstantUserIds();
-            final int[] updateUserIds = request.getUpdateBroadcastUserIds();
-            final int[] instantUserIds = request.getUpdateBroadcastInstantUserIds();
 
-            Bundle extras = new Bundle();
-            extras.putInt(Intent.EXTRA_UID, request.getAppId());
-            if (update) {
-                extras.putBoolean(Intent.EXTRA_REPLACING, true);
-            }
-            if (archived) {
-                extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
-            }
-            extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-
-            // If a package is a static shared library, then only the installer of the package
-            // should get the broadcast.
-            if (installerPackageName != null
-                    && request.getPkg().getStaticSharedLibraryName() != null) {
-                mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, 0 /*flags*/,
-                        installerPackageName, null /*finishedReceiver*/,
-                        request.getNewUsers(), null /* instantUserIds*/,
-                        null /* broadcastAllowList */, null);
-            }
-
-            // Send installed broadcasts if the package is not a static shared lib.
             if (request.getPkg().getStaticSharedLibraryName() == null) {
                 mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
-
-                // Send PACKAGE_ADDED broadcast for users that see the package for the first time
-                // sendPackageAddedForNewUsers also deals with system apps
-                int appId = UserHandle.getAppId(request.getAppId());
-                boolean isSystem = request.isInstallSystem();
-                mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), packageName,
-                        isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId,
-                        firstUserIds, firstInstantUserIds, archived, dataLoaderType);
-
-                // Send PACKAGE_ADDED broadcast for users that don't see
-                // the package for the first time
-
-                // Send to all running apps.
-                final SparseArray<int[]> newBroadcastAllowList;
-                synchronized (mPm.mLock) {
-                    final Computer snapshot = mPm.snapshotComputer();
-                    newBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(snapshot,
-                            snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
-                            updateUserIds, mPm.mSettings.getPackagesLocked());
-                }
-                mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, 0 /*flags*/,
-                        null /*targetPackage*/, null /*finishedReceiver*/,
-                        updateUserIds, instantUserIds, newBroadcastAllowList, null);
-                // Send to the installer, even if it's not running.
-                if (installerPackageName != null) {
-                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, 0 /*flags*/,
-                            installerPackageName, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
-                // Send to PermissionController for all update users, even if it may not be running
-                // for some users
-                if (BroadcastHelper.isPrivacySafetyLabelChangeNotificationsEnabled(mContext)) {
-                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                            extras, 0 /*flags*/,
-                            mPm.mRequiredPermissionControllerPackage, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds, null /* broadcastAllowList */, null);
-                }
-                // Notify required verifier(s) that are not the installer of record for the package.
-                for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
-                    if (verifierPackageName != null && !verifierPackageName.equals(
-                            installerPackageName)) {
-                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                                extras, 0 /*flags*/,
-                                verifierPackageName, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /* broadcastAllowList */,
-                                null);
-                    }
-                }
-                // If package installer is defined, notify package installer about new
-                // app installed
-                mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
-                        extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
-                        mPm.mRequiredInstallerPackage, null /*finishedReceiver*/,
-                        firstUserIds, instantUserIds, null /* broadcastAllowList */, null);
-
-                // Send replaced for users that don't see the package for the first time
-                if (update) {
-                    mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                            packageName, extras, 0 /*flags*/,
-                            null /*targetPackage*/, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds,
-                            request.getRemovedInfo().mBroadcastAllowList, null);
-                    if (installerPackageName != null) {
-                        mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
-                                extras, 0 /*flags*/,
-                                installerPackageName, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds, null /*broadcastAllowList*/,
-                                null);
-                    }
-                    for (String verifierPackageName : mPm.mRequiredVerifierPackages) {
-                        if (verifierPackageName != null && !verifierPackageName.equals(
-                                installerPackageName)) {
-                            mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                                    packageName, extras, 0 /*flags*/, verifierPackageName,
-                                    null /*finishedReceiver*/, updateUserIds, instantUserIds,
-                                    null /*broadcastAllowList*/, null);
-                        }
-                    }
-                    mPm.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
-                            null /*package*/, null /*extras*/, 0 /*flags*/,
-                            packageName /*targetPackage*/,
-                            null /*finishedReceiver*/, updateUserIds, instantUserIds,
-                            null /*broadcastAllowList*/,
-                            mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
-                                    REASON_PACKAGE_REPLACED).toBundle());
-                } else if (launchedForRestore && !request.isInstallSystem()) {
-                    // First-install and we did a restore, so we're responsible for the
-                    // first-launch broadcast.
-                    if (DEBUG_BACKUP) {
-                        Slog.i(TAG, "Post-restore of " + packageName
-                                + " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
-                    }
-                    mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
-                            firstUserIds, firstInstantUserIds);
-                }
-
-                // Send broadcast package appeared if external for all users
-                if (request.getPkg().isExternalStorage()) {
-                    if (!update) {
-                        final StorageManager storageManager =
-                                mInjector.getSystemService(StorageManager.class);
-                        VolumeInfo volume =
-                                storageManager.findVolumeByUuid(
-                                        StorageManager.convert(
-                                                request.getPkg().getVolumeUuid()).toString());
-                        int packageExternalStorageType =
-                                PackageManagerServiceUtils.getPackageExternalStorageType(volume,
-                                        request.getPkg().isExternalStorage());
-                        // If the package was installed externally, log it.
-                        if (packageExternalStorageType != StorageEnums.UNKNOWN) {
-                            FrameworkStatsLog.write(
-                                    FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
-                                    packageExternalStorageType, packageName);
-                        }
-                    }
-                    if (DEBUG_INSTALL) {
-                        Slog.i(TAG, "upgrading pkg " + request.getPkg() + " is external");
-                    }
-                    if (!archived) {
-                        final String[] pkgNames = new String[]{packageName};
-                        final int[] uids = new int[]{request.getPkg().getUid()};
-                        mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
-                                true /* mediaStatus */, true /* replacing */, pkgNames, uids);
-                        mPm.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */,
-                                pkgNames, uids);
-                    }
-                }
-            } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib
-                // No need to kill consumers if it's installation of new version static shared lib.
-                final Computer snapshot = mPm.snapshotComputer();
-                final boolean dontKillApp = !update
-                        && request.getPkg().getStaticSharedLibraryName() != null;
-                for (int i = 0; i < request.getLibraryConsumers().size(); i++) {
-                    AndroidPackage pkg = request.getLibraryConsumers().get(i);
-                    // send broadcast that all consumers of the static shared library have changed
-                    mPm.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), dontKillApp,
-                            new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
-                            pkg.getUid(), null);
-                }
             }
 
+            mBroadcastHelper.sendPostInstallBroadcasts(mPm.snapshotComputer(), request, packageName,
+                    mPm.mRequiredPermissionControllerPackage, mPm.mRequiredVerifierPackages,
+                    mPm.mRequiredInstallerPackage,
+                    /* packageSender= */ mPm, launchedForRestore, killApp, update, archived);
+
+
             // Work that needs to happen on first install within each user
             if (firstUserIds.length > 0) {
                 for (int userId : firstUserIds) {
@@ -3075,7 +2886,6 @@
             }
 
             if (!archived) {
-                final Computer snapshot = mPm.snapshotComputer();
                 // Notify DexManager that the package was installed for new users.
                 // The updated users should already be indexed and the package code paths
                 // should not change.
@@ -3091,7 +2901,7 @@
                 }
             } else {
                 // Now send PACKAGE_REMOVED + EXTRA_REPLACING broadcast.
-                final PackageRemovedInfo info = new PackageRemovedInfo(mPm);
+                final PackageRemovedInfo info = new PackageRemovedInfo();
                 info.mRemovedPackage = packageName;
                 info.mInstallerPackageName = request.getInstallerPackageName();
                 info.mRemovedUsers = firstUserIds;
@@ -3100,8 +2910,8 @@
                 info.mRemovedPackageVersionCode = request.getPkg().getLongVersionCode();
                 info.mRemovedForAllUsers = true;
 
-                info.sendPackageRemovedBroadcasts(false /*killApp*/,
-                        false /*removedBySystem*/, true /*isArchived*/);
+                mBroadcastHelper.sendPackageRemovedBroadcasts(info, mPm,
+                        false /*killApp*/, false /*removedBySystem*/, true /*isArchived*/);
             }
         }
 
@@ -3292,15 +3102,14 @@
         synchronized (mPm.mLock) {
             mPm.mSettings.disableSystemPackageLPw(stubPkg.getPackageName(), true /*replaced*/);
         }
-        final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
-        removePackageHelper.removePackage(stubPkg, true /*chatty*/);
+        mRemovePackageHelper.removePackage(stubPkg, true /*chatty*/);
         try {
             return initPackageTracedLI(scanFile, parseFlags, scanFlags);
         } catch (PackageManagerException e) {
             Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
                     e);
             // Remove the failed install
-            removePackageHelper.removeCodePath(scanFile);
+            mRemovePackageHelper.removeCodePath(scanFile);
             throw e;
         }
     }
@@ -3343,7 +3152,7 @@
             if (!dstCodePath.exists()) {
                 return null;
             }
-            new RemovePackageHelper(mPm).removeCodePath(dstCodePath);
+            mRemovePackageHelper.removeCodePath(dstCodePath);
             return null;
         }
 
@@ -4299,8 +4108,8 @@
                         parsedPackage.getPackageName(), UserHandle.USER_ALL,
                         "scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER,
                         null /* request */)) {
-                    DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
-                    deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
+                    mDeletePackageHelper.deletePackageLIF(
+                            parsedPackage.getPackageName(), null, true,
                             mPm.mUserManager.getUserIds(), 0, null, false);
                 }
             } else if (newPkgVersionGreater || newSharedUserSetting) {
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index fe6a8a1..ca8dc29 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -94,8 +94,6 @@
     private final UserHandle mUser;
     @NonNull
     final PackageManagerService mPm;
-    final InstallPackageHelper mInstallPackageHelper;
-    final RemovePackageHelper mRemovePackageHelper;
     final boolean mIsInherit;
     final int mSessionId;
     final int mRequireUserAction;
@@ -108,8 +106,6 @@
             PackageLite packageLite, PackageManagerService pm) {
         mPm = pm;
         mUser = user;
-        mInstallPackageHelper = new InstallPackageHelper(mPm);
-        mRemovePackageHelper = new RemovePackageHelper(mPm);
         mOriginInfo = originInfo;
         mMoveInfo = moveInfo;
         mObserver = observer;
@@ -142,8 +138,6 @@
             PackageLite packageLite, PackageManagerService pm) {
         mPm = pm;
         mUser = user;
-        mInstallPackageHelper = new InstallPackageHelper(mPm);
-        mRemovePackageHelper = new RemovePackageHelper(mPm);
         mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
         mMoveInfo = null;
         mInstallReason = fixUpInstallReason(
@@ -242,7 +236,7 @@
         // state can change within this delay and hence we need to re-verify certain conditions.
         boolean isStaged = (mInstallFlags & INSTALL_STAGED) != 0;
         if (isStaged) {
-            Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+            Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
                     pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
             mRet = ret.first;
             if (mRet != INSTALL_SUCCEEDED) {
@@ -540,39 +534,39 @@
                 }
             }
         } else {
-            mInstallPackageHelper.installPackagesTraced(installRequests);
+            mPm.installPackagesTraced(installRequests);
 
             for (InstallRequest request : installRequests) {
                 doPostInstall(request);
             }
         }
         for (InstallRequest request : installRequests) {
-            mInstallPackageHelper.restoreAndPostInstall(request);
+            mPm.restoreAndPostInstall(request);
         }
     }
 
     private void doPostInstall(InstallRequest request) {
         if (mMoveInfo != null) {
             if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
-                mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
+                mPm.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
                         mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
             } else {
-                mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mToUuid,
+                mPm.cleanUpForMoveInstall(mMoveInfo.mToUuid,
                         mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
             }
         } else {
             if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
-                mRemovePackageHelper.removeCodePath(request.getCodeFile());
+                mPm.removeCodePath(request.getCodeFile());
             }
         }
     }
 
     private void cleanUpForFailedInstall(InstallRequest request) {
         if (request.isInstallMove()) {
-            mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
+            mPm.cleanUpForMoveInstall(request.getMoveToUuid(),
                     request.getMovePackageName(), request.getMoveFromCodePath());
         } else {
-            mRemovePackageHelper.removeCodePath(request.getCodeFile());
+            mPm.removeCodePath(request.getCodeFile());
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index b4ca477..4ecbd15 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -60,14 +60,10 @@
  */
 final class PackageHandler extends Handler {
     private final PackageManagerService mPm;
-    private final InstallPackageHelper mInstallPackageHelper;
-    private final RemovePackageHelper mRemovePackageHelper;
 
     PackageHandler(Looper looper, PackageManagerService pm) {
         super(looper);
         mPm = pm;
-        mInstallPackageHelper = new InstallPackageHelper(mPm);
-        mRemovePackageHelper = new RemovePackageHelper(mPm);
     }
 
     @Override
@@ -82,7 +78,7 @@
     void doHandleMessage(Message msg) {
         switch (msg.what) {
             case SEND_PENDING_BROADCAST: {
-                mInstallPackageHelper.sendPendingBroadcasts();
+                mPm.sendPendingBroadcasts();
                 break;
             }
             case POST_INSTALL: {
@@ -96,7 +92,7 @@
                 request.onInstallCompleted();
                 request.runPostInstallRunnable();
                 if (!request.isInstallExistingForUser()) {
-                    mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
+                    mPm.handlePackagePostInstall(request, didRestore);
                 } else if (DEBUG_INSTALL) {
                     // No post-install when we run restore from installExistingPackageForUser
                     Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
@@ -107,7 +103,7 @@
             case DEFERRED_NO_KILL_POST_DELETE: {
                 InstallArgs args = (InstallArgs) msg.obj;
                 if (args != null) {
-                    mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
+                    mPm.cleanUpResources(args.mCodeFile, args.mInstructionSets);
                 }
             } break;
             case DEFERRED_NO_KILL_INSTALL_OBSERVER:
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 95b565d..1bb20b47 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -18,7 +18,9 @@
 
 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO;
 import static android.os.Process.INVALID_UID;
+
 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -407,11 +409,10 @@
     }
 
     private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
-        final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
         // Clean up orphaned staging directories
         for (File stage : stagingDirsToRemove) {
             Slog.w(TAG, "Deleting orphan stage " + stage);
-            removePackageHelper.removeCodePath(stage);
+            mPm.removeCodePath(stage);
         }
     }
 
@@ -1320,9 +1321,8 @@
     @Override
     public void installExistingPackage(String packageName, int installFlags, int installReason,
             IntentSender statusReceiver, int userId, List<String> allowListedPermissions) {
-        final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
 
-        var result = installPackageHelper.installExistingPackageAsUser(packageName, userId,
+        var result = mPm.installExistingPackageAsUser(packageName, userId,
                 installFlags, installReason, allowListedPermissions, statusReceiver);
 
         int returnCode = result.first;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 54a2e3ad..d0e5f96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2449,14 +2449,13 @@
     }
 
     private void onSystemDataLoaderUnrecoverable() {
-        final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
         final String packageName = getPackageName();
         if (TextUtils.isEmpty(packageName)) {
             // The package has not been installed.
             return;
         }
         mHandler.post(() -> {
-            if (deletePackageHelper.deletePackageX(packageName,
+            if (mPm.deletePackageX(packageName,
                     PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
                     PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/)
                     != PackageManager.DELETE_SUCCEEDED) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerException.java b/services/core/java/com/android/server/pm/PackageManagerException.java
index dea6659..d69737a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerException.java
+++ b/services/core/java/com/android/server/pm/PackageManagerException.java
@@ -63,6 +63,7 @@
     public static final int INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS = -35;
     public static final int INTERNAL_ERROR_APEX_NOT_DIRECTORY = -36;
     public static final int INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE = -37;
+    public static final int INTERNAL_ERROR_MISSING_USER = -38;
 
     @IntDef(prefix = { "INTERNAL_ERROR_" }, value = {
             INTERNAL_ERROR_NATIVE_LIBRARY_COPY,
@@ -101,7 +102,8 @@
             INTERNAL_ERROR_STATIC_SHARED_LIB_PROTECTED_BROADCAST,
             INTERNAL_ERROR_STATIC_SHARED_LIB_OVERLAY_TARGETS,
             INTERNAL_ERROR_APEX_NOT_DIRECTORY,
-            INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE
+            INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE,
+            INTERNAL_ERROR_MISSING_USER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InternalErrorCode {}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 33cb85c..ddc8369 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -70,7 +70,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
@@ -98,6 +97,7 @@
 import android.content.pm.InstantAppRequest;
 import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.ComponentEnabledSetting;
@@ -115,6 +115,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.UserPackage;
 import android.content.pm.VerifierDeviceIdentity;
+import android.content.pm.VerifierInfo;
 import android.content.pm.VersionedPackage;
 import android.content.pm.overlay.OverlayPaths;
 import android.content.pm.parsing.PackageLite;
@@ -985,7 +986,7 @@
     private final DeletePackageHelper mDeletePackageHelper;
     private final InitAppsHelper mInitAppsHelper;
     private final AppDataHelper mAppDataHelper;
-    private final InstallPackageHelper mInstallPackageHelper;
+    @NonNull private final InstallPackageHelper mInstallPackageHelper;
     private final PreferredActivityHelper mPreferredActivityHelper;
     private final ResolveIntentHelper mResolveIntentHelper;
     private final DexOptHelper mDexOptHelper;
@@ -1715,7 +1716,8 @@
                 (i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
                         i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
                         context),
-                (i, pm) -> new UpdateOwnershipHelper());
+                (i, pm) -> new UpdateOwnershipHelper(),
+                (i, pm) -> new PackageMonitorCallbackHelper());
 
         if (Build.VERSION.SDK_INT <= 0) {
             Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -2067,17 +2069,19 @@
         mDomainVerificationManager.setConnection(mDomainVerificationConnection);
 
         mBroadcastHelper = new BroadcastHelper(mInjector);
-        mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(mInjector);
+        mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
         mAppDataHelper = new AppDataHelper(this);
-        mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
-        mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
-        mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper);
+        mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper);
+        mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
+                mBroadcastHelper);
+        mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper,
+                mDeletePackageHelper, mBroadcastHelper);
 
         mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
                 mInjector.getUserManagerInternal(), mDeletePackageHelper);
 
         mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
-        mPreferredActivityHelper = new PreferredActivityHelper(this);
+        mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper);
         mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
                 injector.getCompatibility(), mUserManager, mDomainVerificationManager,
                 mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
@@ -2085,7 +2089,7 @@
         mDexOptHelper = new DexOptHelper(this);
         mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mUserManager,
                 mBroadcastHelper, mProtectedPackages);
-        mDistractingPackageHelper = new DistractingPackageHelper(this, mInjector, mBroadcastHelper,
+        mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
                 mSuspendPackageHelper);
         mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
                 mRemovePackageHelper);
@@ -3078,38 +3082,6 @@
     }
 
     @Override
-    public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
-            final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
-            final int[] userIds, int[] instantUserIds,
-            @Nullable SparseArray<int[]> broadcastAllowList,
-            @Nullable Bundle bOptions) {
-        mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags,
-                targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
-                null /* filterExtrasForReceiver */, bOptions));
-        if (targetPkg == null) {
-            // For some broadcast action, e.g. ACTION_PACKAGE_ADDED, this method will be called
-            // many times to different targets, e.g. installer app, permission controller, other
-            // registered apps. We should filter it to avoid calling back many times for the same
-            // action. When the targetPkg is set, it sends the broadcast to specific app, e.g.
-            // installer app or null for registered apps. The callback only need to send back to the
-            // registered apps so we check the null condition here.
-            notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList);
-        }
-    }
-
-    void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds,
-            int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
-        mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds,
-                instantUserIds, broadcastAllowList);
-    }
-
-    void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
-            @NonNull String[] pkgNames, @NonNull int[] uids) {
-        mPackageMonitorCallbackHelper.notifyResourcesChanged(mediaStatus, replacing, pkgNames,
-                uids);
-    }
-
-    @Override
     public void notifyPackageAdded(String packageName, int uid) {
         mPackageObserverHelper.notifyAdded(packageName, uid);
     }
@@ -3125,64 +3097,6 @@
         UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName);
     }
 
-    void sendPackageAddedForUser(@NonNull Computer snapshot, String packageName,
-            @NonNull PackageStateInternal packageState, int userId, boolean isArchived,
-            int dataLoaderType) {
-        final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId);
-        final boolean isSystem = packageState.isSystem();
-        final boolean isInstantApp = userState.isInstantApp();
-        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
-        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
-        sendPackageAddedForNewUsers(snapshot, packageName, isSystem /*sendBootCompleted*/,
-                false /*startReceiver*/, packageState.getAppId(), userIds, instantUserIds,
-                isArchived, dataLoaderType);
-
-        // Send a session commit broadcast
-        final PackageInstaller.SessionInfo info = new PackageInstaller.SessionInfo();
-        info.installReason = userState.getInstallReason();
-        info.appPackageName = packageName;
-        sendSessionCommitBroadcast(info, userId);
-    }
-
-    @Override
-    public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
-            boolean sendBootCompleted, boolean includeStopped, @AppIdInt int appId, int[] userIds,
-            int[] instantUserIds, boolean isArchived, int dataLoaderType) {
-        if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
-            return;
-        }
-        SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(snapshot,
-                snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID),
-                userIds, snapshot.getPackageStates());
-        mHandler.post(
-                () -> mBroadcastHelper.sendPackageAddedForNewUsers(packageName, appId, userIds,
-                        instantUserIds, isArchived, dataLoaderType, broadcastAllowList));
-        mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds,
-                instantUserIds, isArchived, dataLoaderType, broadcastAllowList);
-        if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
-            mHandler.post(() -> {
-                        for (int userId : userIds) {
-                            mBroadcastHelper.sendBootCompletedBroadcastToSystemApp(
-                                    packageName, includeStopped, userId);
-                        }
-                    }
-            );
-        }
-    }
-
-    private void sendApplicationHiddenForUser(String packageName, PackageStateInternal packageState,
-            int userId) {
-        final PackageRemovedInfo info = new PackageRemovedInfo(this);
-        info.mRemovedPackage = packageName;
-        info.mInstallerPackageName = packageState.getInstallSource().mInstallerPackageName;
-        info.mRemovedUsers = new int[] {userId};
-        info.mBroadcastUsers = new int[] {userId};
-        info.mUid = UserHandle.getUid(userId, packageState.getAppId());
-        info.mRemovedPackageVersionCode = packageState.getVersionCode();
-        info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/,
-                false /*isArchived*/);
-    }
-
     boolean isUserRestricted(int userId, String restrictionKey) {
         Bundle restrictions = mUserManager.getUserRestrictions(userId);
         if (restrictions.getBoolean(restrictionKey, false)) {
@@ -3505,11 +3419,6 @@
         }
     }
 
-    void postPreferredActivityChangedBroadcast(int userId) {
-        mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
-    }
-
-
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     @GuardedBy("mLock")
     void clearPackagePreferredActivitiesLPw(String packageName,
@@ -3609,17 +3518,8 @@
     }
 
     public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
-        UserManagerService ums = UserManagerService.getInstance();
-        if (ums == null || sessionInfo.isStaged()) {
-            return;
-        }
-        final UserInfo parent = ums.getProfileParent(userId);
-        final int launcherUid = (parent != null) ? parent.id : userId;
-        // TODO: Should this snapshot be moved further up?
-        final ComponentName launcherComponent = snapshotComputer()
-                .getDefaultHomeActivity(launcherUid);
-        mBroadcastHelper.sendSessionCommitBroadcast(sessionInfo, userId, launcherUid,
-                launcherComponent, mAppPredictionServicePackage);
+        mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId,
+                mAppPredictionServicePackage);
     }
 
     private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
@@ -3988,7 +3888,7 @@
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
-                if (!mInstallPackageHelper.enableCompressedPackage(deletedPkg, pkgSetting)) {
+                if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
                     Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
                             + "commpressed package " + setting.getPackageName());
                     updateAllowed[i] = false;
@@ -4070,8 +3970,8 @@
                 final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
                 final int packageUid = UserHandle.getUid(
                         userId, pkgSettings.get(packageName).getAppId());
-                sendPackageChangedBroadcast(newSnapshot, packageName, false /* dontKillApp */,
-                        components, packageUid, null /* reason */);
+                mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName,
+                        false /* dontKillApp */, components, packageUid, null /* reason */);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -4147,27 +4047,6 @@
         }
     }
 
-    void sendPackageChangedBroadcast(@NonNull Computer snapshot, String packageName,
-            boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
-        PackageStateInternal setting = snapshot.getPackageStateInternal(packageName,
-                Process.SYSTEM_UID);
-        if (setting == null) {
-            return;
-        }
-        final int userId = UserHandle.getUserId(packageUid);
-        final boolean isInstantApp =
-                snapshot.isInstantAppInternal(packageName, userId, Process.SYSTEM_UID);
-        final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
-        final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
-        final SparseArray<int[]> broadcastAllowList =
-                isInstantApp ? null : snapshot.getVisibilityAllowLists(packageName, userIds);
-        mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast(
-                packageName, dontKillApp, componentNames, packageUid, reason, userIds,
-                instantUserIds, broadcastAllowList));
-        mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames,
-                packageUid, reason, userIds, instantUserIds, broadcastAllowList);
-    }
-
     /**
      * Used by SystemServer
      */
@@ -4312,7 +4191,7 @@
                 if (pkg == null) {
                     return;
                 }
-                sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
+                mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
                         true /* dontKillApp */,
                         new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
                         pkg.getUid(),
@@ -5294,7 +5173,7 @@
                 throw new SecurityException("Calling package " + packageName
                         + " does not belong to calling uid " + callingUid);
             }
-            return mSuspendPackageHelper
+            return SuspendPackageHelper
                     .getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid);
         }
 
@@ -5857,10 +5736,14 @@
                 if (hidden) {
                     killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg",
                             ApplicationExitInfo.REASON_OTHER);
-                    sendApplicationHiddenForUser(packageName, newPackageState, userId);
+                    mBroadcastHelper.sendApplicationHiddenForUser(
+                            packageName, newPackageState, userId,
+                            /* packageSender= */ PackageManagerService.this);
                 } else {
-                    sendPackageAddedForUser(newSnapshot, packageName, newPackageState, userId,
-                            false /* isArchived */, DataLoaderType.NONE);
+                    mBroadcastHelper.sendPackageAddedForUser(
+                            newSnapshot, packageName, newPackageState, userId,
+                            false /* isArchived */, DataLoaderType.NONE,
+                            mAppPredictionServicePackage);
                 }
 
                 scheduleWritePackageRestrictions(userId);
@@ -7929,4 +7812,75 @@
             }
         }
     }
+
+    void removeCodePath(@Nullable File codePath) {
+        mRemovePackageHelper.removeCodePath(codePath);
+    }
+
+    void cleanUpResources(@Nullable File codeFile, @Nullable String[] instructionSets) {
+        mRemovePackageHelper.cleanUpResources(codeFile, instructionSets);
+    }
+
+    void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) {
+        mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath);
+    }
+
+    void sendPendingBroadcasts() {
+        mInstallPackageHelper.sendPendingBroadcasts();
+    }
+
+    void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) {
+        mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore);
+    }
+
+    Pair<Integer, IntentSender> installExistingPackageAsUser(
+            @Nullable String packageName,
+            @UserIdInt int userId, @PackageManager.InstallFlags int installFlags,
+            @PackageManager.InstallReason int installReason,
+            @Nullable List<String> allowlistedRestrictedPermissions,
+            @Nullable IntentSender intentSender) {
+        return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
+                installReason, allowlistedRestrictedPermissions, intentSender);
+    }
+    AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
+            throws PackageManagerException {
+        return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags);
+    }
+
+    void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action,
+                                         @NonNull int[] allUserHandles,
+                                         boolean writeSettings) throws SystemDeleteException {
+        mInstallPackageHelper.restoreDisabledSystemPackageLIF(
+                action, allUserHandles, writeSettings);
+    }
+    boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg,
+                                    @NonNull PackageSetting stubPs) {
+        return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs);
+    }
+
+    void installPackagesTraced(List<InstallRequest> requests) {
+        mInstallPackageHelper.installPackagesTraced(requests);
+    }
+
+    void restoreAndPostInstall(InstallRequest request) {
+        mInstallPackageHelper.restoreAndPostInstall(request);
+    }
+
+    Pair<Integer, String> verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite,
+                                    long requiredInstalledVersionCode,
+                                    int installFlags) {
+        return mInstallPackageHelper.verifyReplacingVersionCode(
+                pkgLite, requiredInstalledVersionCode, installFlags);
+    }
+
+    int getUidForVerifier(VerifierInfo verifierInfo) {
+        return mInstallPackageHelper.getUidForVerifier(verifierInfo);
+    }
+
+    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
+                        boolean removedBySystem) {
+        return mDeletePackageHelper.deletePackageX(packageName,
+                PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
+                PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 0c2e082..5b770aab 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -146,6 +146,7 @@
     private final Singleton<SharedLibrariesImpl> mSharedLibrariesProducer;
     private final Singleton<CrossProfileIntentFilterHelper> mCrossProfileIntentFilterHelperProducer;
     private final Singleton<UpdateOwnershipHelper> mUpdateOwnershipHelperProducer;
+    private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
 
     PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
             Installer installer, Object installLock, PackageAbiHelper abiHelper,
@@ -186,7 +187,8 @@
             Producer<IBackupManager> iBackupManager,
             Producer<SharedLibrariesImpl> sharedLibrariesProducer,
             Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
-            Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
+            Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
+            Producer<PackageMonitorCallbackHelper> packageMonitorCallbackHelper) {
         mContext = context;
         mLock = lock;
         mInstaller = installer;
@@ -242,6 +244,7 @@
         mCrossProfileIntentFilterHelperProducer = new Singleton<>(
                 crossProfileIntentFilterHelperProducer);
         mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
+        mPackageMonitorCallbackHelper = new Singleton<>(packageMonitorCallbackHelper);
     }
 
     /**
@@ -431,6 +434,10 @@
         return mUpdateOwnershipHelperProducer.get(this, mPackageManager);
     }
 
+    public PackageMonitorCallbackHelper getPackageMonitorCallbackHelper() {
+        return mPackageMonitorCallbackHelper.get(this, mPackageManager);
+    }
+
 
     /** Provides an abstraction to static access to system state. */
     public interface SystemWrapper {
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index bb3bf53..b8c2b86 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -52,12 +52,6 @@
     private final Object mLock = new Object();
     final IActivityManager mActivityManager = ActivityManager.getService();
 
-    final Handler mHandler;
-
-    PackageMonitorCallbackHelper(PackageManagerServiceInjector injector) {
-        mHandler = injector.getHandler();
-    }
-
     @NonNull
     @GuardedBy("mLock")
     private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
@@ -100,7 +94,8 @@
 
     public void notifyPackageAddedForNewUsers(String packageName,
             @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds,
-            boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList) {
+            boolean isArchived, int dataLoaderType, SparseArray<int[]> broadcastAllowList,
+            @NonNull Handler handler) {
         Bundle extras = new Bundle(2);
         // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
         final int uid = UserHandle.getUid(
@@ -111,11 +106,11 @@
         }
         extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
         notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras ,
-                userIds /* userIds */, instantUserIds, broadcastAllowList);
+                userIds /* userIds */, instantUserIds, broadcastAllowList, handler);
     }
 
     public void notifyResourcesChanged(boolean mediaStatus, boolean replacing,
-            @NonNull String[] pkgNames, @NonNull int[] uids) {
+            @NonNull String[] pkgNames, @NonNull int[] uids, @NonNull Handler handler) {
         Bundle extras = new Bundle();
         extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgNames);
         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
@@ -125,12 +120,12 @@
         String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                 : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
         notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, handler);
     }
 
     public void notifyPackageChanged(String packageName, boolean dontKillApp,
             ArrayList<String> componentNames, int packageUid, String reason, int[] userIds,
-            int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+            int[] instantUserIds, SparseArray<int[]> broadcastAllowList, Handler handler) {
         Bundle extras = new Bundle(4);
         extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
         String[] nameList = new String[componentNames.size()];
@@ -142,11 +137,12 @@
             extras.putString(Intent.EXTRA_REASON, reason);
         }
         notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds,
-                instantUserIds, broadcastAllowList);
+                instantUserIds, broadcastAllowList, handler);
     }
 
     public void notifyPackageMonitor(String action, String pkg, Bundle extras,
-            int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+            int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
+            Handler handler) {
         if (!isAllowedCallbackAction(action)) {
             return;
         }
@@ -160,9 +156,10 @@
             }
 
             if (ArrayUtils.isEmpty(instantUserIds)) {
-                doNotifyCallbacks(action, pkg, extras, resolvedUserIds, broadcastAllowList);
+                doNotifyCallbacks(
+                        action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
             } else {
-                doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList);
+                doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler);
             }
         } catch (RemoteException e) {
             // do nothing
@@ -181,7 +178,7 @@
     }
 
     private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds,
-            SparseArray<int[]> broadcastAllowList) {
+            SparseArray<int[]> broadcastAllowList, Handler handler) {
         RemoteCallbackList<IRemoteCallback> callbacks;
         synchronized (mLock) {
             callbacks = mCallbacks;
@@ -202,7 +199,7 @@
             final int[] allowUids =
                     broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{};
 
-            mHandler.post(() -> callbacks.broadcast((callback, user) -> {
+            handler.post(() -> callbacks.broadcast((callback, user) -> {
                 RegisterUser registerUser = (RegisterUser) user;
                 if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
                         != userId)) {
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 9f02542..7ee1772 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -16,24 +16,12 @@
 
 package com.android.server.pm;
 
-import static android.os.PowerExemptionManager.REASON_PACKAGE_REPLACED;
-import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
-import android.annotation.NonNull;
-import android.app.ActivityManagerInternal;
-import android.app.BroadcastOptions;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.PowerExemptionManager;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
 
 final class PackageRemovedInfo {
-    final PackageSender mPackageSender;
     String mRemovedPackage;
     String mInstallerPackageName;
     int mUid = -1;
@@ -58,116 +46,6 @@
     InstallArgs mArgs = null;
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
-    PackageRemovedInfo(PackageSender packageSender) {
-        mPackageSender = packageSender;
-    }
-
-    void sendPackageRemovedBroadcasts(boolean killApp, boolean removedBySystem,
-            boolean isArchived) {
-        sendPackageRemovedBroadcastInternal(killApp, removedBySystem, isArchived);
-    }
-
-    void sendSystemPackageUpdatedBroadcasts() {
-        if (mIsRemovedPackageSystemUpdate) {
-            sendSystemPackageUpdatedBroadcastsInternal();
-        }
-    }
-
-    private void sendSystemPackageUpdatedBroadcastsInternal() {
-        Bundle extras = new Bundle(2);
-        extras.putInt(Intent.EXTRA_UID, mRemovedAppId >= 0 ? mRemovedAppId : mUid);
-        extras.putBoolean(Intent.EXTRA_REPLACING, true);
-        mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, mRemovedPackage, extras,
-                0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
-        if (mInstallerPackageName != null) {
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
-                    mRemovedPackage, extras, 0 /*flags*/,
-                    mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
-                    null);
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                    mRemovedPackage, extras, 0 /*flags*/,
-                    mInstallerPackageName, null, null, null, null /* broadcastAllowList */,
-                    null);
-        }
-        mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, mRemovedPackage,
-                extras, 0, null /*targetPackage*/, null, null, null, mBroadcastAllowList, null);
-        mPackageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
-                mRemovedPackage, null, null, null, null /* broadcastAllowList */,
-                getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED).toBundle());
-    }
-
-    private static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
-            @PowerExemptionManager.ReasonCode int reasonCode) {
-        long duration = 10_000;
-        final ActivityManagerInternal amInternal =
-                LocalServices.getService(ActivityManagerInternal.class);
-        if (amInternal != null) {
-            duration = amInternal.getBootTimeTempAllowListDuration();
-        }
-        final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
-        bOptions.setTemporaryAppAllowlist(duration,
-                TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
-                reasonCode, "");
-        return bOptions;
-    }
-
-    private void sendPackageRemovedBroadcastInternal(boolean killApp, boolean removedBySystem,
-            boolean isArchived) {
-        Bundle extras = new Bundle();
-        final int removedUid = mRemovedAppId >= 0  ? mRemovedAppId : mUid;
-        extras.putInt(Intent.EXTRA_UID, removedUid);
-        extras.putBoolean(Intent.EXTRA_DATA_REMOVED, mDataRemoved);
-        extras.putBoolean(Intent.EXTRA_SYSTEM_UPDATE_UNINSTALL, mIsRemovedPackageSystemUpdate);
-        extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, !killApp);
-        extras.putBoolean(Intent.EXTRA_USER_INITIATED, !removedBySystem);
-        final boolean isReplace = mIsUpdate || mIsRemovedPackageSystemUpdate;
-        if (isReplace || isArchived) {
-            extras.putBoolean(Intent.EXTRA_REPLACING, true);
-        }
-        if (isArchived) {
-            extras.putBoolean(Intent.EXTRA_ARCHIVAL, true);
-        }
-        extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, mRemovedForAllUsers);
-
-        // Send PACKAGE_REMOVED broadcast to the respective installer.
-        if (mRemovedPackage != null && mInstallerPackageName != null) {
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
-                    mRemovedPackage, extras, 0 /*flags*/,
-                    mInstallerPackageName, null, mBroadcastUsers, mInstantUserIds, null, null);
-        }
-        if (mIsStaticSharedLib) {
-            // When uninstalling static shared libraries, only the package's installer needs to be
-            // sent a PACKAGE_REMOVED broadcast. There are no other intended recipients.
-            return;
-        }
-        if (mRemovedPackage != null) {
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
-                    mRemovedPackage, extras, 0, null /*targetPackage*/, null,
-                    mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
-                    mRemovedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
-                    null /*finishedReceiver*/, mBroadcastUsers, mInstantUserIds,
-                    mBroadcastAllowList, null /*bOptions*/);
-            if (mDataRemoved && !mIsRemovedPackageSystemUpdate) {
-                mPackageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
-                        mRemovedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
-                        null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
-                mPackageSender.notifyPackageRemoved(mRemovedPackage, removedUid);
-            }
-        }
-        if (mRemovedAppId >= 0) {
-            // If a system app's updates are uninstalled the UID is not actually removed. Some
-            // services need to know the package name affected.
-            if (isReplace) {
-                extras.putString(Intent.EXTRA_PACKAGE_NAME, mRemovedPackage);
-            }
-
-            mPackageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
-                    null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
-                    null, null, mBroadcastUsers, mInstantUserIds, mBroadcastAllowList, null);
-        }
-    }
-
     public void populateBroadcastUsers(PackageSetting deletedPackageSetting) {
         if (mRemovedUsers == null) {
             mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/PackageSender.java b/services/core/java/com/android/server/pm/PackageSender.java
index 82e1d5f3..db83f59 100644
--- a/services/core/java/com/android/server/pm/PackageSender.java
+++ b/services/core/java/com/android/server/pm/PackageSender.java
@@ -16,24 +16,7 @@
 
 package com.android.server.pm;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.IIntentReceiver;
-import android.os.Bundle;
-import android.util.SparseArray;
-
 interface PackageSender {
-    /**
-     * @param userIds User IDs where the action occurred on a full application
-     * @param instantUserIds User IDs where the action occurred on an instant application
-     */
-    void sendPackageBroadcast(String action, String pkg,
-            Bundle extras, int flags, String targetPkg,
-            IIntentReceiver finishedReceiver, int[] userIds, int[] instantUserIds,
-            @Nullable SparseArray<int[]> broadcastAllowList, @Nullable Bundle bOptions);
-    void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
-            boolean sendBootCompleted, boolean includeStopped, int appId, int[] userIds,
-            int[] instantUserIds, boolean isArchived, int dataLoaderType);
     void notifyPackageAdded(String packageName, int uid);
     void notifyPackageChanged(String packageName, int uid);
     void notifyPackageRemoved(String packageName, int uid);
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 571aab4..41d2aeb 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -69,10 +69,12 @@
     private static final String TAG_DEFAULT_APPS = "da";
 
     private final PackageManagerService mPm;
+    private final BroadcastHelper mBroadcastHelper;
 
     // TODO(b/198166813): remove PMS dependency
-    PreferredActivityHelper(PackageManagerService pm) {
+    PreferredActivityHelper(PackageManagerService pm, BroadcastHelper broadcastHelper) {
         mPm = pm;
+        mBroadcastHelper = broadcastHelper;
     }
 
     private ResolveInfo findPreferredActivityNotLocked(@NonNull Computer snapshot, Intent intent,
@@ -120,7 +122,7 @@
         }
         if (changedUsers.size() > 0) {
             updateDefaultHomeNotLocked(mPm.snapshotComputer(), changedUsers);
-            mPm.postPreferredActivityChangedBroadcast(userId);
+            mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
             mPm.scheduleWritePackageRestrictions(userId);
         }
     }
@@ -167,7 +169,7 @@
         return mPm.setActiveLauncherPackage(packageName, userId,
                 successful -> {
                     if (successful) {
-                        mPm.postPreferredActivityChangedBroadcast(userId);
+                        mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
                     }
                 });
     }
@@ -215,7 +217,7 @@
         }
         // Re-snapshot after mLock
         if (!(isHomeFilter(filter) && updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId))) {
-            mPm.postPreferredActivityChangedBroadcast(userId);
+            mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
         }
     }
 
@@ -411,7 +413,7 @@
         if (isHomeFilter(filter)) {
             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
         }
-        mPm.postPreferredActivityChangedBroadcast(userId);
+        mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
     }
 
     public void clearPackagePersistentPreferredActivities(String packageName, int userId) {
@@ -426,7 +428,7 @@
         }
         if (changed) {
             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
-            mPm.postPreferredActivityChangedBroadcast(userId);
+            mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
             mPm.scheduleWritePackageRestrictions(userId);
         }
     }
@@ -443,7 +445,7 @@
         }
         if (changed) {
             updateDefaultHomeNotLocked(mPm.snapshotComputer(), userId);
-            mPm.postPreferredActivityChangedBroadcast(userId);
+            mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
             mPm.scheduleWritePackageRestrictions(userId);
         }
     }
@@ -616,7 +618,7 @@
                 mPm.clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
             }
             if (changedUsers.size() > 0) {
-                mPm.postPreferredActivityChangedBroadcast(userId);
+                mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId);
             }
             synchronized (mPm.mLock) {
                 mPm.mSettings.applyDefaultPreferredAppsLPw(userId);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index d989c90..b055a3f 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -22,6 +22,7 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
+
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
@@ -69,9 +70,11 @@
     private final PermissionManagerServiceInternal mPermissionManager;
     private final SharedLibrariesImpl mSharedLibraries;
     private final AppDataHelper mAppDataHelper;
+    private final BroadcastHelper mBroadcastHelper;
 
     // TODO(b/198166813): remove PMS dependency
-    RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
+    RemovePackageHelper(PackageManagerService pm, AppDataHelper appDataHelper,
+                        BroadcastHelper broadcastHelper) {
         mPm = pm;
         mIncrementalManager = mPm.mInjector.getIncrementalManager();
         mInstaller = mPm.mInjector.getInstaller();
@@ -79,10 +82,7 @@
         mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
         mSharedLibraries = mPm.mInjector.getSharedLibrariesImpl();
         mAppDataHelper = appDataHelper;
-    }
-
-    RemovePackageHelper(PackageManagerService pm) {
-        this(pm, new AppDataHelper(pm));
+        mBroadcastHelper = broadcastHelper;
     }
 
     public void removeCodePath(File codePath) {
@@ -265,7 +265,8 @@
 
         final List<AndroidPackage> sharedUserPkgs =
                 sus != null ? sus.getPackages() : Collections.emptyList();
-        final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
+        final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm,
+                mBroadcastHelper);
         final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
                 : new int[] {userId};
         for (int nextUserId : userIds) {
@@ -395,13 +396,13 @@
             }
             if (changedUsers.size() > 0) {
                 final PreferredActivityHelper preferredActivityHelper =
-                        new PreferredActivityHelper(mPm);
+                        new PreferredActivityHelper(mPm, mBroadcastHelper);
                 preferredActivityHelper.updateDefaultHomeNotLocked(mPm.snapshotComputer(),
                         changedUsers);
-                mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
+                mBroadcastHelper.sendPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
             }
         } else if (!deletedPs.isSystem() && outInfo != null && !outInfo.mIsUpdate
-                && outInfo.mRemovedUsers != null) {
+                && outInfo.mRemovedUsers != null && !outInfo.mIsExternal) {
             // For non-system uninstalls with DELETE_KEEP_DATA, set the installed state to false
             // for affected users. This does not apply to app updates where the old apk is replaced
             // but the old data remains.
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 0ea45c4..e993d9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -372,6 +372,7 @@
         // Extract Icon and update the icon res ID and the bitmap path.
         s.saveIconAndFixUpShortcutLocked(this, newShortcut);
         s.fixUpShortcutResourceNamesAndValues(newShortcut);
+        ensureShortcutCountBeforePush();
         saveShortcut(newShortcut);
     }
 
@@ -426,7 +427,6 @@
             @NonNull List<ShortcutInfo> changedShortcuts) {
         Preconditions.checkArgument(newShortcut.isEnabled(),
                 "pushDynamicShortcuts() cannot publish disabled shortcuts");
-        ensureShortcutCountBeforePush();
 
         newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
 
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index db5b9b1..c725cdc 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -147,7 +147,6 @@
 
         final Settings.VersionInfo ver;
         final List<? extends PackageStateInternal> packages;
-        final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm);
         synchronized (mPm.mLock) {
             ver = mPm.mSettings.findOrCreateVersion(volumeUuid);
             packages = mPm.mSettings.getVolumePackagesLPr(volumeUuid);
@@ -160,7 +159,7 @@
             synchronized (mPm.mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = installPackageHelper.initPackageTracedLI(
+                    pkg = mPm.initPackageTracedLI(
                             ps.getPath(), parseFlags, SCAN_INITIAL);
                     loaded.add(pkg);
 
@@ -228,7 +227,8 @@
         }
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
-        sendResourcesChangedBroadcast(true /* mediaStatus */, false /* replacing */, loaded);
+        mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+                true /* mediaStatus */, false /* replacing */, loaded);
         synchronized (mLoadedVolumes) {
             mLoadedVolumes.add(vol.getId());
         }
@@ -256,7 +256,7 @@
 
                     final AndroidPackage pkg = ps.getPkg();
                     final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
-                    final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
+                    final PackageRemovedInfo outInfo = new PackageRemovedInfo();
 
                     try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
                              UserHandle.USER_ALL, deleteFlags,
@@ -280,7 +280,8 @@
         }
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
-        sendResourcesChangedBroadcast(false /* mediaStatus */, false /* replacing */, unloaded);
+        mBroadcastHelper.sendResourcesChangedBroadcastAndNotify(mPm.snapshotComputer(),
+                false /* mediaStatus */, false /* replacing */, unloaded);
         synchronized (mLoadedVolumes) {
             mLoadedVolumes.remove(vol.getId());
         }
@@ -295,21 +296,6 @@
         }
     }
 
-    private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
-            ArrayList<AndroidPackage> packages) {
-        final int size = packages.size();
-        final String[] packageNames = new String[size];
-        final int[] packageUids = new int[size];
-        for (int i = 0; i < size; i++) {
-            final AndroidPackage pkg = packages.get(i);
-            packageNames[i] = pkg.getPackageName();
-            packageUids[i] = pkg.getUid();
-        }
-        mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer, mediaStatus,
-                replacing, packageNames, packageUids);
-        mPm.notifyResourcesChanged(mediaStatus, replacing, packageNames, packageUids);
-    }
-
     /**
      * Examine all apps present on given mounted volume, and destroy apps that
      * aren't expected, either due to uninstallation or reinstallation on
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index ddb045d..29d99a73 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -26,17 +26,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.AppOpsManager;
-import android.app.BroadcastOptions;
-import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
 import android.content.pm.SuspendDialogInfo;
 import android.os.Binder;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
@@ -47,7 +43,6 @@
 import android.util.IntArray;
 import android.util.Slog;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.server.LocalServices;
@@ -207,19 +202,22 @@
             }
         });
 
+        final Computer newSnapshot = mPm.snapshotComputer();
         if (!notifyPackagesList.isEmpty()) {
             final String[] changedPackages =
                     notifyPackagesList.toArray(new String[0]);
-            sendPackagesSuspendedForUser(
+            mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
                     suspended ? Intent.ACTION_PACKAGES_SUSPENDED
                             : Intent.ACTION_PACKAGES_UNSUSPENDED,
                     changedPackages, notifyUids.toArray(), quarantined, userId);
-            sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
+            mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, changedPackages,
+                    suspended, userId);
             mPm.scheduleWritePackageRestrictions(userId);
         }
         // Send the suspension changed broadcast to ensure suspension state is not stale.
         if (!changedPackagesList.isEmpty()) {
-            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
+            mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+                    Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
                     changedPackagesList.toArray(new String[0]), changedUids.toArray(), quarantined,
                     userId);
         }
@@ -269,8 +267,10 @@
      * @return The app extras of the suspended package.
      */
     @Nullable
-    Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot, @NonNull String packageName,
-            int userId, int callingUid) {
+    static Bundle getSuspendedPackageAppExtras(@NonNull Computer snapshot,
+                                               @NonNull String packageName,
+                                               int userId,
+                                               int callingUid) {
         final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, callingUid);
         if (ps == null) {
             return null;
@@ -299,7 +299,7 @@
      *                                   suspensions will be removed.
      * @param userId The user for which the changes are taking place.
      */
-    void removeSuspensionsBySuspendingPackage(@NonNull Computer computer,
+    void removeSuspensionsBySuspendingPackage(@NonNull Computer snapshot,
             @NonNull String[] packagesToChange,
             @NonNull Predicate<String> suspendingPackagePredicate, int userId) {
         final List<String> unsuspendedPackages = new ArrayList<>();
@@ -307,7 +307,7 @@
         final ArrayMap<String, ArraySet<String>> pkgToSuspendingPkgsToCommit = new ArrayMap<>();
         for (String packageName : packagesToChange) {
             final PackageStateInternal packageState =
-                    computer.getPackageStateInternal(packageName);
+                    snapshot.getPackageStateInternal(packageName);
             final PackageUserStateInternal packageUserState = packageState == null
                     ? null : packageState.getUserStateOrDefault(userId);
             if (packageUserState == null || !packageUserState.isSuspended()) {
@@ -350,11 +350,14 @@
         });
 
         mPm.scheduleWritePackageRestrictions(userId);
+        final Computer newSnapshot = mPm.snapshotComputer();
         if (!unsuspendedPackages.isEmpty()) {
             final String[] packageArray = unsuspendedPackages.toArray(
                     new String[unsuspendedPackages.size()]);
-            sendMyPackageSuspendedOrUnsuspended(packageArray, false, userId);
-            sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_UNSUSPENDED,
+            mBroadcastHelper.sendMyPackageSuspendedOrUnsuspended(newSnapshot, packageArray,
+                    false, userId);
+            mBroadcastHelper.sendPackagesSuspendedOrUnsuspendedForUser(newSnapshot,
+                    Intent.ACTION_PACKAGES_UNSUSPENDED,
                     packageArray, unsuspendedUids.toArray(), false, userId);
         }
     }
@@ -610,38 +613,6 @@
     }
 
     /**
-     * Send broadcast intents for packages suspension changes.
-     *
-     * @param intent The action name of the suspension intent.
-     * @param pkgList The names of packages which have suspension changes.
-     * @param uidList The uids of packages which have suspension changes.
-     * @param userId The user where packages reside.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    void sendPackagesSuspendedForUser(@NonNull String intent, @NonNull String[] pkgList,
-            @NonNull int[] uidList, boolean quarantined, int userId) {
-        final Handler handler = mInjector.getHandler();
-        final Bundle extras = new Bundle(3);
-        extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
-        extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
-        if (quarantined) {
-            extras.putBoolean(Intent.EXTRA_QUARANTINED, true);
-        }
-        final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND;
-        final Bundle options = new BroadcastOptions()
-                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
-                .toBundle();
-        handler.post(() -> mBroadcastHelper.sendPackageBroadcast(intent, null /* pkg */,
-                extras, flags, null /* targetPkg */, null /* finishedReceiver */,
-                new int[]{userId}, null /* instantUserIds */, null /* broadcastAllowList */,
-                (callingUid, intentExtras) -> BroadcastHelper.filterExtrasChangedPackageList(
-                        mPm.snapshotComputer(), callingUid, intentExtras),
-                options));
-        mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId},
-                null /* instantUserIds */, null /* broadcastAllowList */);
-    }
-
-    /**
      * Suspends packages on behalf of an admin.
      *
      * @return array of packages that are unsuspendable, either because admin is not allowed to
@@ -756,37 +727,4 @@
         }
         return false;
     }
-
-    private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
-            int userId) {
-        final Handler handler = mInjector.getHandler();
-        final String action = suspended
-                ? Intent.ACTION_MY_PACKAGE_SUSPENDED
-                : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
-        handler.post(() -> {
-            final IActivityManager am = ActivityManager.getService();
-            if (am == null) {
-                Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
-                        + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
-                return;
-            }
-            final int[] targetUserIds = new int[] {userId};
-            final Computer snapshot = mPm.snapshotComputer();
-            for (String packageName : affectedPackages) {
-                final Bundle appExtras = suspended
-                        ? getSuspendedPackageAppExtras(snapshot, packageName, userId, SYSTEM_UID)
-                        : null;
-                final Bundle intentExtras;
-                if (appExtras != null) {
-                    intentExtras = new Bundle(1);
-                    intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
-                } else {
-                    intentExtras = null;
-                }
-                mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
-                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                        targetUserIds, false, null, null, null);
-            }
-        });
-    }
 }
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 8c73ce8..c6435ae 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -139,7 +139,6 @@
     private final UserHandle mUser;
     @NonNull
     private final PackageManagerService mPm;
-    private final InstallPackageHelper mInstallPackageHelper;
 
     VerifyingSession(UserHandle user, File stagedDir, IPackageInstallObserver2 observer,
             PackageInstaller.SessionParams sessionParams, InstallSource installSource,
@@ -147,7 +146,6 @@
             boolean userActionRequired, PackageManagerService pm) {
         mPm = pm;
         mUser = user;
-        mInstallPackageHelper = new InstallPackageHelper(mPm);
         mOriginInfo = OriginInfo.fromStagedFile(stagedDir);
         mObserver = observer;
         mInstallFlags = sessionParams.installFlags;
@@ -181,7 +179,7 @@
         PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
                 mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
 
-        Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
+        Pair<Integer, String> ret = mPm.verifyReplacingVersionCode(
                 pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
         setReturnCode(ret.first, ret.second);
         if (mRet != INSTALL_SUCCEEDED) {
@@ -729,7 +727,7 @@
                 continue;
             }
 
-            final int verifierUid = mInstallPackageHelper.getUidForVerifier(verifierInfo);
+            final int verifierUid = mPm.getUidForVerifier(verifierInfo);
             if (verifierUid == -1) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dc75a98..dfc9b8b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -198,6 +198,7 @@
 import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
+import com.android.internal.display.BrightnessUtils;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -217,7 +218,6 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemServiceManager;
 import com.android.server.UiThread;
-import com.android.server.display.BrightnessUtils;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.input.KeyboardMetricsCollector;
 import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
@@ -1983,7 +1983,6 @@
             public void run() {
                 if (mPendingHomeKeyEvent != null) {
                     handleShortPressOnHome(mPendingHomeKeyEvent);
-                    mPendingHomeKeyEvent.recycle();
                     mPendingHomeKeyEvent = null;
                 }
             }
@@ -2028,7 +2027,7 @@
                     if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_PIP_MENU
                             || mPictureInPictureVisible) {
                         mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
-                        mPendingHomeKeyEvent = KeyEvent.obtain(event);
+                        mPendingHomeKeyEvent = event;
                         mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
                                 ViewConfiguration.getDoubleTapTimeout());
                         return true;
@@ -2036,11 +2035,7 @@
                 }
 
                 // Post to main thread to avoid blocking input pipeline.
-                final KeyEvent shortPressEvent = KeyEvent.obtain(event);
-                mHandler.post(() -> {
-                    handleShortPressOnHome(shortPressEvent);
-                    shortPressEvent.recycle();
-                });
+                mHandler.post(() -> handleShortPressOnHome(event));
                 return true;
             }
 
@@ -2067,14 +2062,9 @@
             if (repeatCount == 0) {
                 mHomePressed = true;
                 if (mPendingHomeKeyEvent != null) {
-                    mPendingHomeKeyEvent.recycle();
                     mPendingHomeKeyEvent = null;
                     mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
-                    final KeyEvent doublePressEvent = KeyEvent.obtain(event);
-                    mHandler.post(() -> {
-                        handleDoubleTapOnHome(doublePressEvent);
-                        doublePressEvent.recycle();
-                    });
+                    mHandler.post(() -> handleDoubleTapOnHome(event));
                 // TODO(multi-display): Remove display id check once we support recents on
                 // multi-display
                 } else if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI
@@ -2084,11 +2074,7 @@
             } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
                 if (!keyguardOn) {
                     // Post to main thread to avoid blocking input pipeline.
-                    final KeyEvent longPressEvent = KeyEvent.obtain(event);
-                    mHandler.post(() -> {
-                        handleLongPressOnHome(longPressEvent);
-                        longPressEvent.recycle();
-                    });
+                    mHandler.post(() -> handleLongPressOnHome(event));
                 }
             }
             return true;
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 96f4a01..c2666f6 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -297,7 +297,8 @@
                     return;
                 }
 
-                for (ClientState clientState : mClients.values()) {
+
+                for (ClientState clientState : mClients.values().toArray(new ClientState[0])) {
                     tryRespondWithError(
                             clientState.mDelegatingListener.mRemoteListener,
                             SpeechRecognizer.ERROR_SERVER_DISCONNECTED);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 40e9c13..88eaafa 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -955,6 +955,17 @@
         }
     }
 
+    public void setTiles(String tiles) {
+        enforceStatusBarOrShell();
+
+        if (mBar != null) {
+            try {
+                mBar.setQsTiles(tiles.split(","));
+            } catch (RemoteException ex) {
+            }
+        }
+    }
+
     public void clickTile(ComponentName component) {
         enforceStatusBarOrShell();
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index 11a4976d..d6bf02f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -61,6 +61,8 @@
                     return runAddTile();
                 case "remove-tile":
                     return runRemoveTile();
+                case "set-tiles":
+                    return runSetTiles();
                 case "click-tile":
                     return runClickTile();
                 case "check-support":
@@ -105,6 +107,11 @@
         return 0;
     }
 
+    private int runSetTiles() throws RemoteException {
+        mInterface.setTiles(getNextArgRequired());
+        return 0;
+    }
+
     private int runClickTile() throws RemoteException {
         mInterface.clickTile(ComponentName.unflattenFromString(getNextArgRequired()));
         return 0;
@@ -242,6 +249,9 @@
         pw.println("  remove-tile COMPONENT");
         pw.println("    Remove a TileService of the specified component");
         pw.println("");
+        pw.println("  set-tiles LIST-OF-TILES");
+        pw.println("    Sets the list of tiles as the current Quick Settings tiles");
+        pw.println("");
         pw.println("  click-tile COMPONENT");
         pw.println("    Click on a TileService of the specified component");
         pw.println("");
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index f33ecaa..184de58 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -301,9 +301,12 @@
         }
 
         // Always update the reordering time when this is called to ensure that the timeout
-        // is reset
+        // is reset.  Extend this duration when running in tests.
+        final long timeout = ActivityManager.isRunningInUserTestHarness()
+                ? mFreezeTaskListTimeoutMs * 10
+                : mFreezeTaskListTimeoutMs;
         mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
-        mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, mFreezeTaskListTimeoutMs);
+        mService.mH.postDelayed(mResetFreezeTaskListOnTimeoutRunnable, timeout);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a6d285a..3faf8b9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5241,16 +5241,17 @@
             applyForcedPropertiesForDefaultDisplay();
             mAnimator.ready();
             mDisplayReady = true;
-            // Reconfigure all displays to make sure that forced properties and
-            // DisplayWindowSettings are applied.
-            mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
+            mHasWideColorGamutSupport = queryWideColorGamutSupport();
+            mHasHdrSupport = queryHdrSupport();
             mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TOUCHSCREEN);
             mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_FAKETOUCH);
+            // Reconfigure all displays to make sure that the forced properties and
+            // DisplayWindowSettings are applied. In addition, wide-color/hdr/isTouchDevice also
+            // affect the Configuration.
+            mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
         }
-
-        mAtmService.updateConfiguration(null /* request to compute config */);
     }
 
     public void systemReady() {
@@ -5258,8 +5259,6 @@
         mPolicy.systemReady();
         mRoot.forAllDisplayPolicies(DisplayPolicy::systemReady);
         mSnapshotController.systemReady();
-        mHasWideColorGamutSupport = queryWideColorGamutSupport();
-        mHasHdrSupport = queryHdrSupport();
         UiThread.getHandler().post(mSettingsObserver::loadSettings);
         IVrManager vrManager = IVrManager.Stub.asInterface(
                 ServiceManager.getService(Context.VR_SERVICE));
@@ -9647,4 +9646,15 @@
             Binder.restoreCallingIdentity(origId);
         }
     }
+
+    /**
+     * Resets the spatial ordering of recents for testing purposes.
+     */
+    void resetFreezeRecentTaskListReordering() {
+        if (!checkCallingPermission(MANAGE_ACTIVITY_TASKS,
+                "resetFreezeRecentTaskListReordering()")) {
+            throw new SecurityException("Requires MANAGE_ACTIVITY_TASKS permission");
+        }
+        mAtmService.getRecentTasks().resetFreezeTaskListReorderingOnTimeout();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 8fad950..fa9a65f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -142,6 +142,8 @@
                     return runReset(pw);
                 case "disable-blur":
                     return runSetBlurDisabled(pw);
+                case "reset-freeze-recent-tasks":
+                    return runResetFreezeRecentTaskListReordering(pw);
                 case "shell":
                     return runWmShellCommand(pw);
                 default:
@@ -252,6 +254,11 @@
         return 0;
     }
 
+    private int runResetFreezeRecentTaskListReordering(PrintWriter pw) throws RemoteException {
+        mInternal.resetFreezeRecentTaskListReordering();
+        return 0;
+    }
+
     private void printInitialDisplayDensity(PrintWriter pw , int displayId) {
         try {
             final int initialDensity = mInterface.getInitialDisplayDensity(displayId);
@@ -1492,6 +1499,8 @@
         printLetterboxHelp(pw);
         printMultiWindowConfigHelp(pw);
 
+        pw.println("  reset-freeze-recent-tasks");
+        pw.println("    Resets the spatial ordering of the recent tasks list");
         pw.println("  reset [-d DISPLAY_ID]");
         pw.println("    Reset all override settings.");
         if (!IS_USER) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d0ead14..25e8475 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -21,6 +21,7 @@
 import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY;
 import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_CONFLICTING_ADMIN_POLICY;
 import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_HARDWARE_LIMITATION;
+import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;
 import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
 import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
 import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
@@ -51,6 +52,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -63,6 +65,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.devicepolicy.flags.FlagUtils;
 import com.android.server.utils.Slogf;
 
 import libcore.io.IoUtils;
@@ -117,6 +120,10 @@
      * Map containing the current set of admins in each user with active policies.
      */
     private final SparseArray<Set<EnforcingAdmin>> mEnforcingAdmins;
+    private final SparseArray<HashMap<EnforcingAdmin, Integer>> mAdminPolicySize;
+
+    //TODO(b/295504706) : Speak to security team to decide what to set Policy_Size_Limit
+    private static final int POLICY_SIZE_LIMIT = 99999;
 
     private final DeviceAdminServiceController mDeviceAdminServiceController;
 
@@ -131,6 +138,7 @@
         mLocalPolicies = new SparseArray<>();
         mGlobalPolicies = new HashMap<>();
         mEnforcingAdmins = new SparseArray<>();
+        mAdminPolicySize = new SparseArray<>();
     }
 
     /**
@@ -139,7 +147,6 @@
      *
      * <p>If {@code skipEnforcePolicy} is true, it sets the policies in the internal data structure
      * but doesn't call the enforcing logic.
-     *
      */
     <V> void setLocalPolicy(
             @NonNull PolicyDefinition<V> policyDefinition,
@@ -152,6 +159,12 @@
 
         synchronized (mLock) {
             PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
+            if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+                if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+                        policyDefinition, userId)) {
+                    return;
+                }
+            }
 
             if (policyDefinition.isNonCoexistablePolicy()) {
                 setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -236,6 +249,7 @@
     }
 
     // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
     /**
      * Set the policy for the provided {@code policyDefinition}
      * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -250,6 +264,7 @@
     }
 
     // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
     /**
      * Removes any previously set policy for the provided {@code policyDefinition}
      * (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -267,6 +282,10 @@
             }
             PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
 
+            if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+                decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
+            }
+
             if (policyDefinition.isNonCoexistablePolicy()) {
                 setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
                         enforcingAdmin, /* value= */ null, userId, /* skipEnforcePolicy= */ false);
@@ -392,6 +411,7 @@
     }
 
     // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
     /**
      * Set the policy for the provided {@code policyDefinition}
      * (see {@link PolicyDefinition}) and {@code enforcingAdmin} to the provided {@code value}.
@@ -407,6 +427,13 @@
         Objects.requireNonNull(value);
 
         synchronized (mLock) {
+            PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
+            if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+                if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+                        policyDefinition, UserHandle.USER_ALL)) {
+                    return;
+                }
+            }
             // TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
             //  that honors the restriction once there's an API available
             if (checkFor2gFailure(policyDefinition, enforcingAdmin)) {
@@ -416,8 +443,6 @@
                 return;
             }
 
-            PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
-
             boolean policyChanged = globalPolicyState.addPolicy(enforcingAdmin, value);
             boolean policyAppliedOnAllUsers = applyGlobalPolicyOnUsersWithLocalPoliciesLocked(
                     policyDefinition, enforcingAdmin, value, skipEnforcePolicy);
@@ -434,7 +459,7 @@
                 // TODO(b/285532044): remove hack and handle properly
                 if (!policyAppliedGlobally
                         && policyDefinition.getPolicyKey().getIdentifier().equals(
-                                USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
+                        USER_CONTROL_DISABLED_PACKAGES_POLICY)) {
                     PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value;
                     PolicyValue<Set<String>> parsedResolvedValue =
                             (PolicyValue<Set<String>>) globalPolicyState.getCurrentResolvedPolicy();
@@ -459,6 +484,7 @@
     }
 
     // TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
+
     /**
      * Removes any previously set policy for the provided {@code policyDefinition}
      * (see {@link PolicyDefinition}) and {@code enforcingAdmin}.
@@ -472,6 +498,11 @@
 
         synchronized (mLock) {
             PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
+
+            if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+                decreasePolicySizeForAdmin(policyState, enforcingAdmin);
+            }
+
             boolean policyChanged = policyState.removePolicy(enforcingAdmin);
 
             if (policyChanged) {
@@ -687,7 +718,6 @@
      * <p>Note that this will always return at most one item for policies that do not require
      * additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
      * {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
-     *
      */
     @NonNull
     <V> Set<PolicyKey> getLocalPolicyKeysSetByAdmin(
@@ -723,7 +753,6 @@
      * <p>Note that this will always return at most one item for policies that do not require
      * additional params (e.g. {@link PolicyDefinition#LOCK_TASK} vs
      * {@link PolicyDefinition#PERMISSION_GRANT(String, String)}).
-     *
      */
     @NonNull
     <V> Set<PolicyKey> getLocalPolicyKeysSetByAllAdmins(
@@ -964,7 +993,7 @@
             EnforcingAdmin callingAdmin,
             PolicyDefinition<V> policyDefinition,
             int userId) {
-        for (EnforcingAdmin admin: policyState.getPoliciesSetByAdmins().keySet()) {
+        for (EnforcingAdmin admin : policyState.getPoliciesSetByAdmins().keySet()) {
             // We're sending a separate broadcast for the calling admin with the result.
             if (admin.equals(callingAdmin)) {
                 continue;
@@ -1152,7 +1181,7 @@
                     try {
                         if (packageManager.getPackageInfo(packageName, 0, userId) == null
                                 || packageManager.getActivityInfo(
-                                        policies.get(admin).getValue(), 0, userId) == null) {
+                                policies.get(admin).getValue(), 0, userId) == null) {
                             Slogf.e(TAG, String.format(
                                     "Persistent preferred activity in package %s not found for "
                                             + "user %d, removing policy for admin",
@@ -1450,6 +1479,97 @@
         return false;
     }
 
+    /**
+     * Calculate the size of a policy in bytes
+     */
+
+    private static <V> int sizeOf(PolicyValue<V> value) {
+        try {
+            Parcel parcel = Parcel.obtain();
+            parcel.writeParcelable(value, /* flags= */ 0);
+
+            parcel.setDataPosition(0);
+
+            byte[] bytes;
+
+            bytes = parcel.marshall();
+            return bytes.length;
+        } catch (Exception e) {
+            Log.e(TAG, "Error calculating size of policy: " + e);
+            return 0;
+        }
+    }
+
+    /**
+     * Checks if the policy already exists and removes the current size to prevent recording the
+     * same policy twice.
+     *
+     * Checks if the new sum of the size of all policies is less than the maximum sum of policies
+     * size per admin and returns true.
+     *
+     * If the policy size limit is reached then send policy result to admin and return false.
+     */
+
+    private <V> boolean handleAdminPolicySizeLimit(PolicyState<V> policyState, EnforcingAdmin admin,
+            PolicyValue<V> value, PolicyDefinition policyDefinition, int userId) {
+        int currentSize = 0;
+        if (mAdminPolicySize.contains(admin.getUserId())
+                && mAdminPolicySize.get(
+                admin.getUserId()).containsKey(admin)) {
+            currentSize = mAdminPolicySize.get(admin.getUserId()).get(admin);
+        }
+        if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+            currentSize -= sizeOf(policyState.getPoliciesSetByAdmins().get(admin));
+        }
+        int policySize = sizeOf(value);
+        if (currentSize + policySize < POLICY_SIZE_LIMIT) {
+            increasePolicySizeForAdmin(admin, policySize);
+            return true;
+        } else {
+            sendPolicyResultToAdmin(
+                    admin,
+                    policyDefinition,
+                    RESULT_FAILURE_STORAGE_LIMIT_REACHED,
+                    userId);
+            return false;
+        }
+    }
+
+    /**
+     * Increase the int in mAdminPolicySize representing the size of the sum of all
+     * active policies for that admin.
+     */
+
+    private <V> void increasePolicySizeForAdmin(EnforcingAdmin admin, int policySize) {
+        if (!mAdminPolicySize.contains(admin.getUserId())) {
+            mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+        }
+        if (!mAdminPolicySize.get(admin.getUserId()).containsKey(admin)) {
+            mAdminPolicySize.get(admin.getUserId()).put(admin, /* size= */ 0);
+        }
+        mAdminPolicySize.get(admin.getUserId()).put(admin,
+                mAdminPolicySize.get(admin.getUserId()).get(admin) + policySize);
+    }
+
+    /**
+     * Decrease the int in mAdminPolicySize representing the size of the sum of all
+     * active policies for that admin.
+     */
+
+    private <V> void decreasePolicySizeForAdmin(PolicyState<V> policyState, EnforcingAdmin admin) {
+        if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+            mAdminPolicySize.get(admin.getUserId()).put(admin,
+                    mAdminPolicySize.get(admin.getUserId()).get(admin) - sizeOf(
+                            policyState.getPoliciesSetByAdmins().get(admin)));
+        }
+        if (mAdminPolicySize.get(admin.getUserId()).get(admin) <= 0) {
+            mAdminPolicySize.get(admin.getUserId()).remove(admin);
+        }
+        if (mAdminPolicySize.get(admin.getUserId()).isEmpty()) {
+            mAdminPolicySize.remove(admin.getUserId());
+        }
+    }
+
     @NonNull
     private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
         synchronized (mLock) {
@@ -1508,11 +1628,13 @@
         clear();
         write();
     }
+
     private void clear() {
         synchronized (mLock) {
             mGlobalPolicies.clear();
             mLocalPolicies.clear();
             mEnforcingAdmins.clear();
+            mAdminPolicySize.clear();
         }
     }
 
@@ -1553,7 +1675,11 @@
         private static final String TAG_POLICY_STATE_ENTRY = "policy-state-entry";
         private static final String TAG_POLICY_KEY_ENTRY = "policy-key-entry";
         private static final String TAG_ENFORCING_ADMINS_ENTRY = "enforcing-admins-entry";
+        private static final String TAG_ENFORCING_ADMIN_AND_SIZE = "enforcing-admin-and-size";
+        private static final String TAG_ENFORCING_ADMIN = "enforcing-admin";
+        private static final String TAG_POLICY_SUM_SIZE = "policy-sum-size";
         private static final String ATTR_USER_ID = "user-id";
+        private static final String ATTR_POLICY_SUM_SIZE = "size";
 
         private final File mFile;
 
@@ -1595,6 +1721,7 @@
             writeLocalPoliciesInner(serializer);
             writeGlobalPoliciesInner(serializer);
             writeEnforcingAdminsInner(serializer);
+            writeEnforcingAdminSizeInner(serializer);
         }
 
         private void writeLocalPoliciesInner(TypedXmlSerializer serializer) throws IOException {
@@ -1652,6 +1779,30 @@
             }
         }
 
+        private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
+                throws IOException {
+            if (FlagUtils.isDevicePolicySizeTrackingEnabled()) {
+                if (mAdminPolicySize != null) {
+                    for (int i = 0; i < mAdminPolicySize.size(); i++) {
+                        int userId = mAdminPolicySize.keyAt(i);
+                        for (EnforcingAdmin admin : mAdminPolicySize.get(
+                                userId).keySet()) {
+                            serializer.startTag(/* namespace= */ null,
+                                    TAG_ENFORCING_ADMIN_AND_SIZE);
+                            serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+                            admin.saveToXml(serializer);
+                            serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+                            serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+                            serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+                                    mAdminPolicySize.get(userId).get(admin));
+                            serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+                            serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
+                        }
+                    }
+                }
+            }
+        }
+
         void readFromFileLocked() {
             if (!mFile.exists()) {
                 Log.d(TAG, "" + mFile + " doesn't exist");
@@ -1689,6 +1840,9 @@
                     case TAG_ENFORCING_ADMINS_ENTRY:
                         readEnforcingAdminsInner(parser);
                         break;
+                    case TAG_ENFORCING_ADMIN_AND_SIZE:
+                        readEnforcingAdminAndSizeInner(parser);
+                        break;
                     default:
                         Slogf.wtf(TAG, "Unknown tag " + tag);
                 }
@@ -1767,5 +1921,37 @@
             }
             mEnforcingAdmins.get(admin.getUserId()).add(admin);
         }
+
+        private void readEnforcingAdminAndSizeInner(TypedXmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            int outerDepth = parser.getDepth();
+            EnforcingAdmin admin = null;
+            int size = 0;
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                String tag = parser.getName();
+                switch (tag) {
+                    case TAG_ENFORCING_ADMIN:
+                        admin = EnforcingAdmin.readFromXml(parser);
+                        break;
+                    case TAG_POLICY_SUM_SIZE:
+                        size = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
+                        break;
+                    default:
+                        Slogf.wtf(TAG, "Unknown tag " + tag);
+                }
+            }
+            if (admin == null) {
+                Slogf.wtf(TAG, "Error parsing enforcingAdmins, EnforcingAdmin is null.");
+                return;
+            }
+            if (size <= 0) {
+                Slogf.wtf(TAG, "Error parsing policy size, size is " + size);
+                return;
+            }
+            if (!mAdminPolicySize.contains(admin.getUserId())) {
+                mAdminPolicySize.put(admin.getUserId(), new HashMap<>());
+            }
+            mAdminPolicySize.get(admin.getUserId()).put(admin, size);
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f604932..6aa135a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -241,7 +241,6 @@
 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
 import static android.provider.Telephony.Carriers.INVALID_APN_ID;
 import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
-
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
@@ -23455,7 +23454,6 @@
     public DevicePolicyState getDevicePolicyState() {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS));
-
         return mInjector.binderWithCleanCallingIdentity(mDevicePolicyEngine::getDevicePolicyState);
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
index 9fe3749..7e17ef1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/FlagUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.server.devicepolicy.flags;
 
+import static com.android.server.devicepolicy.flags.Flags.devicePolicySizeTrackingEnabled;
 import static com.android.server.devicepolicy.flags.Flags.policyEngineMigrationV2Enabled;
 
 import android.os.Binder;
@@ -28,4 +29,10 @@
             return policyEngineMigrationV2Enabled();
         });
     }
+
+    public static boolean isDevicePolicySizeTrackingEnabled() {
+        return Binder.withCleanCallingIdentity(() -> {
+            return devicePolicySizeTrackingEnabled();
+        });
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
index 00702a9..0dde496 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/flags/flags.aconfig
@@ -5,4 +5,10 @@
   namespace: "enterprise"
   description: "V2 of the policy engine migrations for Android V"
   bug: "289520697"
+}
+flag {
+  name: "device_policy_size_tracking_enabled"
+  namespace: "enterprise"
+  description: "Add feature to track the total policy size and have a max threshold."
+  bug: "281543351"
 }
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/OWNERS b/services/foldables/devicestateprovider/OWNERS
index b2dcd0c..5732844 100644
--- a/services/foldables/devicestateprovider/OWNERS
+++ b/services/foldables/devicestateprovider/OWNERS
@@ -1,6 +1,6 @@
 akulian@google.com
-kennethford@google.com
 jiamingliu@google.com
 kchyn@google.com
+kennethford@google.com
 nickchameyev@google.com
 nicomazz@google.com
\ No newline at end of file
diff --git a/services/foldables/devicestateprovider/TEST_MAPPING b/services/foldables/devicestateprovider/TEST_MAPPING
index cd0d851..47de131 100644
--- a/services/foldables/devicestateprovider/TEST_MAPPING
+++ b/services/foldables/devicestateprovider/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "foldable-services-tests",
+      "name": "foldable-device-state-provider-tests",
       "options": [
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
diff --git a/services/foldables/devicestateprovider/tests/AndroidTest.xml b/services/foldables/devicestateprovider/tests/AndroidTest.xml
index 55e0d9c..f5fdac7 100644
--- a/services/foldables/devicestateprovider/tests/AndroidTest.xml
+++ b/services/foldables/devicestateprovider/tests/AndroidTest.xml
@@ -22,7 +22,7 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.foldableslib.tests" />
+        <option name="package" value="com.android.foldablesdevicestatelib.tests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
         <option name="hidden-api-checks" value="false" />
     </test>
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 4a2bf75..5d3eba8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -17,24 +17,22 @@
 package com.android.server.pm;
 
 import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
 import static com.google.common.truth.Truth.assertWithMessage;
+
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
+
 import static java.lang.reflect.Modifier.isFinal;
 import static java.lang.reflect.Modifier.isPublic;
 import static java.lang.reflect.Modifier.isStatic;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.AppGlobals;
-import android.content.IIntentReceiver;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Postsubmit;
-import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -87,18 +85,6 @@
     @Test
     public void testPackageRemoval() {
         class PackageSenderImpl implements PackageSender {
-            public void sendPackageBroadcast(final String action, final String pkg,
-                    final Bundle extras, final int flags, final String targetPkg,
-                    final IIntentReceiver finishedReceiver, final int[] userIds,
-                    int[] instantUserIds, SparseArray<int[]> broadcastAllowList,
-                    @Nullable Bundle bOptions) {
-            }
-
-            public void sendPackageAddedForNewUsers(@NonNull Computer snapshot, String packageName,
-                    boolean sendBootComplete, boolean includeStopped, int appId,
-                    int[] userIds, int[] instantUserIds, boolean isArchived, int dataLoaderType) {
-            }
-
             @Override
             public void notifyPackageAdded(String packageName, int uid) {
             }
@@ -113,9 +99,8 @@
             }
         }
 
-        PackageSenderImpl sender = new PackageSenderImpl();
         PackageSetting setting = null;
-        PackageRemovedInfo pri = new PackageRemovedInfo(sender);
+        PackageRemovedInfo pri = new PackageRemovedInfo();
 
         // Initial conditions: nothing there
         Assert.assertNull(pri.mRemovedUsers);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
index 2fd6e5f..7a4327c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessSynchronizerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -27,15 +28,18 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.database.ContentObserver;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.view.Display;
+import android.view.DisplayAdjustments;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
@@ -59,6 +63,7 @@
     private static final float EPSILON = 0.00001f;
     private static final Uri BRIGHTNESS_URI =
             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+    private static final float BRIGHTNESS_MAX = 0.6f;
 
     private Context mContext;
     private MockContentResolver mContentResolverSpy;
@@ -66,6 +71,7 @@
     private DisplayListener mDisplayListener;
     private ContentObserver mContentObserver;
     private TestLooper mTestLooper;
+    private BrightnessSynchronizer mSynchronizer;
 
     @Mock private DisplayManager mDisplayManagerMock;
     @Captor private ArgumentCaptor<DisplayListener> mDisplayListenerCaptor;
@@ -74,7 +80,17 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+
+        Display display = mock(Display.class);
+        when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
+        BrightnessInfo info = new BrightnessInfo(PowerManager.BRIGHTNESS_INVALID_FLOAT,
+                PowerManager.BRIGHTNESS_MIN, BRIGHTNESS_MAX,
+                BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, BRIGHTNESS_MAX,
+                BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+        when(display.getBrightnessInfo()).thenReturn(info);
+
+        mContext = spy(new ContextWrapper(
+                ApplicationProvider.getApplicationContext().createDisplayContext(display)));
         mContentResolverSpy = spy(new MockContentResolver(mContext));
         mContentResolverSpy.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolverSpy);
@@ -128,13 +144,12 @@
     @Test
     public void testSetSameIntValue_nothingUpdated() {
         putFloatSetting(0.5f);
-        putIntSetting(128);
         start();
 
-        putIntSetting(128);
+        putIntSetting(fToI(0.5f));
         advanceTime(10);
         verify(mDisplayManagerMock, times(0)).setBrightness(
-                eq(Display.DEFAULT_DISPLAY), eq(iToF(128)));
+                eq(Display.DEFAULT_DISPLAY), eq(0.5f));
     }
 
     @Test
@@ -154,14 +169,13 @@
         // Verify that this update did not get sent to float, because synchronizer
         // is still waiting for confirmation of its first value.
         verify(mDisplayManagerMock, times(0)).setBrightness(
-                eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+                Display.DEFAULT_DISPLAY, iToF(20));
 
         // Send the confirmation of the initial change. This should trigger the new value to
         // finally be processed and we can verify that the new value (20) is sent.
         putIntSetting(fToI(0.4f));
         advanceTime(10);
-        verify(mDisplayManagerMock).setBrightness(
-                eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+        verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
 
     }
 
@@ -183,8 +197,7 @@
         advanceTime(200);
 
         // Verify that the new value gets sent because the timeout expired.
-        verify(mDisplayManagerMock).setBrightness(
-                eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
+        verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY, iToF(20));
 
         // Send a confirmation of the initial event, BrightnessSynchronizer should treat this as a
         // new event because the timeout had already expired
@@ -196,14 +209,14 @@
 
         // Verify we sent what would have been the confirmation as a new event to displaymanager.
         // We do both fToI and iToF because the conversions are not symmetric.
-        verify(mDisplayManagerMock).setBrightness(
-                eq(Display.DEFAULT_DISPLAY), eq(iToF(fToI(0.4f))));
+        verify(mDisplayManagerMock).setBrightness(Display.DEFAULT_DISPLAY,
+                iToF(fToI(0.4f)));
     }
 
-    private BrightnessSynchronizer start() {
-        BrightnessSynchronizer bs = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
+    private void start() {
+        mSynchronizer = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
                 mClock::now);
-        bs.startSynchronizing();
+        mSynchronizer.startSynchronizing();
         verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(),
                 isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
         mDisplayListener = mDisplayListenerCaptor.getValue();
@@ -211,7 +224,6 @@
         verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false),
                 mContentObserverCaptor.capture(), eq(UserHandle.USER_ALL));
         mContentObserver = mContentObserverCaptor.getValue();
-        return bs;
     }
 
     private int getIntSetting() throws Exception {
@@ -241,11 +253,11 @@
     }
 
     private int fToI(float brightness) {
-        return BrightnessSynchronizer.brightnessFloatToInt(brightness);
+        return mSynchronizer.brightnessFloatToIntSetting(brightness);
     }
 
     private float iToF(int brightness) {
-        return BrightnessSynchronizer.brightnessIntToFloat(brightness);
+        return mSynchronizer.brightnessIntSettingToFloat(brightness);
     }
 
     private void advanceTime(long timeMs) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index a23539e..306de52 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -71,10 +71,12 @@
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.Curve;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -87,11 +89,13 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.Process;
 import android.os.RemoteException;
 import android.view.ContentRecordingSession;
 import android.view.Display;
+import android.view.DisplayAdjustments;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
 import android.view.DisplayInfo;
@@ -99,7 +103,6 @@
 import android.view.SurfaceControl;
 import android.window.DisplayWindowPolicyController;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
@@ -118,11 +121,11 @@
 import com.android.server.sensors.SensorManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
-import com.google.common.truth.Expect;
-
 import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
 import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -198,9 +201,10 @@
 
                 @Override
                 LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
-                        Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+                        Handler handler, DisplayAdapter.Listener displayAdapterListener,
+                        DisplayManagerFlags flags) {
                     return new LocalDisplayAdapter(syncRoot, context, handler,
-                            displayAdapterListener, new LocalDisplayAdapter.Injector() {
+                            displayAdapterListener, flags, new LocalDisplayAdapter.Injector() {
                         @Override
                         public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
                             return mSurfaceControlProxy;
@@ -244,8 +248,14 @@
 
         @Override
         LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
-                Handler handler, DisplayAdapter.Listener displayAdapterListener) {
-            return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+                Handler handler, DisplayAdapter.Listener displayAdapterListener,
+                DisplayManagerFlags flags) {
+            return new LocalDisplayAdapter(
+                    syncRoot,
+                    context,
+                    handler,
+                    displayAdapterListener,
+                    flags,
                     new LocalDisplayAdapter.Injector() {
                         @Override
                         public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
@@ -323,7 +333,11 @@
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal);
         // TODO: b/287945043
-        mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        Display display = mock(Display.class);
+        when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
+        when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
+        mContext = spy(new ContextWrapper(
+                ApplicationProvider.getApplicationContext().createDisplayContext(display)));
         mResources = Mockito.spy(mContext.getResources());
         manageDisplaysPermission(/* granted= */ false);
         when(mContext.getResources()).thenReturn(mResources);
@@ -1898,7 +1912,6 @@
 
     @Test
     public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() {
-        Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
         DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
 
         // get the first two internal displays
@@ -2445,6 +2458,86 @@
         assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED,
                 EVENT_DISPLAY_DISCONNECTED);
     }
+
+    @Test
+    public void testRegisterDisplayOffloader_whenEnabled_DisplayHasDisplayOffloadSession() {
+        when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(true);
+        // set up DisplayManager
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        // set up display
+        FakeDisplayDevice displayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+        initDisplayPowerController(localService);
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        LogicalDisplay display =
+                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+        int displayId = display.getDisplayIdLocked();
+
+        // Register DisplayOffloader.
+        DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+        localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+        assertThat(display.getDisplayOffloadSessionLocked().getDisplayOffloader()).isEqualTo(
+                mockDisplayOffloader);
+    }
+
+    @Test
+    public void testRegisterDisplayOffloader_whenDisabled_DisplayHasNoDisplayOffloadSession() {
+        when(mMockFlags.isDisplayOffloadEnabled()).thenReturn(false);
+        // set up DisplayManager
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerInternal localService = displayManager.new LocalService();
+        // set up display
+        FakeDisplayDevice displayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.DEFAULT_DISPLAY);
+        initDisplayPowerController(localService);
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        LogicalDisplay display =
+                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+        int displayId = display.getDisplayIdLocked();
+
+        // Register DisplayOffloader.
+        DisplayOffloader mockDisplayOffloader = mock(DisplayOffloader.class);
+        localService.registerDisplayOffloader(displayId, mockDisplayOffloader);
+
+        assertThat(display.getDisplayOffloadSessionLocked()).isNull();
+    }
+
+    private void initDisplayPowerController(DisplayManagerInternal localService) {
+        localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() {
+            @Override
+            public void onStateChanged() {
+
+            }
+
+            @Override
+            public void onProximityPositive() {
+
+            }
+
+            @Override
+            public void onProximityNegative() {
+
+            }
+
+            @Override
+            public void onDisplayStateChange(boolean allInactive, boolean allOff) {
+
+            }
+
+            @Override
+            public void acquireSuspendBlocker(String id) {
+
+            }
+
+            @Override
+            public void releaseSuspendBlocker(String id) {
+
+            }
+        }, new Handler(Looper.getMainLooper()), mSensorManager);
+    }
+
     private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
index a56b59a..dca69eb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -44,6 +44,7 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
@@ -123,6 +124,9 @@
     private Handler mHandler;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
+    private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
+
     @Mock
     private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
     @Mock
@@ -1409,6 +1413,111 @@
                 BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
     }
 
+    @Test
+    public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+        // set up.
+        int initState = Display.STATE_DOZE;
+        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with DOZE.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+    }
+
+    @Test
+    public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+        // set up.
+        int initState = Display.STATE_DOZE;
+        int unSupportedTargetState = Display.STATE_ON;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with DOZE.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+    }
+
+    @Test
+    public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+        // set up.
+        int initState = Display.STATE_OFF;
+        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with OFF.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+    }
+
+    private void initDisplayOffloadSession() {
+        mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+            @Override
+            public boolean startOffload() {
+                return true;
+            }
+
+            @Override
+            public void stopOffload() {}
+        });
+
+        mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+            @Override
+            public void setDozeStateOverride(int displayState) {}
+
+            @Override
+            public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+                return mDisplayOffloader;
+            }
+        };
+    }
+
     /**
      * Creates a mock and registers it to {@link LocalServices}.
      */
@@ -1742,9 +1851,9 @@
         BrightnessRangeController getBrightnessRangeController(
                 HighBrightnessModeController hbmController, Runnable modeChangeCallback,
                 DisplayDeviceConfig displayDeviceConfig, Handler handler,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, IBinder displayToken, DisplayDeviceInfo info) {
             return new BrightnessRangeController(hbmController, modeChangeCallback,
-                    displayDeviceConfig, mHdrClamper, mFlags);
+                    displayDeviceConfig, mHdrClamper, mFlags, displayToken, info);
         }
 
         @Override
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 0572117..edaa1d5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -44,6 +44,7 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.os.Handler;
@@ -122,6 +123,8 @@
     private Handler mHandler;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
+    private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
+    private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession;
 
     @Mock
     private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
@@ -1364,6 +1367,110 @@
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
                 BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
     }
+    @Test
+    public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+        // set up.
+        int initState = Display.STATE_DOZE;
+        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with DOZE.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState).setScreenState(supportedTargetState);
+    }
+
+    @Test
+    public void testDozeScreenStateOverride_toUnSupportedOffloadStateFromDoze_stateRemains() {
+        // set up.
+        int initState = Display.STATE_DOZE;
+        int unSupportedTargetState = Display.STATE_ON;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with DOZE.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(unSupportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+    }
+
+    @Test
+    public void testDozeScreenStateOverride_toSupportedOffloadStateFromOFF_stateRemains() {
+        // set up.
+        int initState = Display.STATE_OFF;
+        int supportedTargetState = Display.STATE_DOZE_SUSPEND;
+        mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
+        doAnswer(invocation -> {
+            when(mHolder.displayPowerState.getScreenState()).thenReturn(invocation.getArgument(0));
+            return null;
+        }).when(mHolder.displayPowerState).setScreenState(anyInt());
+        // init displayoffload session and support offloading.
+        initDisplayOffloadSession();
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+
+        // start with OFF.
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(initState);
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_OFF;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState
+
+        mHolder.dpc.overrideDozeScreenState(supportedTargetState);
+        advanceTime(1); // Run updatePowerState
+
+        verify(mHolder.displayPowerState, never()).setScreenState(anyInt());
+    }
+
+    private void initDisplayOffloadSession() {
+        mDisplayOffloader = spy(new DisplayManagerInternal.DisplayOffloader() {
+            @Override
+            public boolean startOffload() {
+                return true;
+            }
+
+            @Override
+            public void stopOffload() {}
+        });
+
+        mDisplayOffloadSession = new DisplayManagerInternal.DisplayOffloadSession() {
+            @Override
+            public void setDozeStateOverride(int displayState) {}
+
+            @Override
+            public DisplayManagerInternal.DisplayOffloader getDisplayOffloader() {
+                return mDisplayOffloader;
+            }
+        };
+    }
 
     private void advanceTime(long timeMs) {
         mClock.fastForward(timeMs);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 6cde5e3..147e8f2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -32,12 +32,15 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
+import android.hardware.display.DisplayManagerInternal.DisplayOffloader;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -55,6 +58,7 @@
 import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.display.LocalDisplayAdapter.BacklightAdapter;
+import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.display.mode.DisplayModeDirector;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
@@ -72,6 +76,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -107,8 +112,15 @@
     private LightsManager mMockedLightsManager;
     @Mock
     private LogicalLight mMockedBacklight;
+    @Mock
+    private DisplayManagerFlags mFlags;
+
     private Handler mHandler;
 
+    private DisplayOffloadSession mDisplayOffloadSession;
+
+    private DisplayOffloader mDisplayOffloader;
+
     private TestListener mListener = new TestListener();
 
     private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>();
@@ -120,6 +132,8 @@
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final int[] BACKLIGHT_RANGE = { 1, 255 };
     private static final float[] BACKLIGHT_RANGE_ZERO_TO_ONE = { 0.0f, 1.0f };
+    private static final List<Integer> mDisplayOffloadSupportedStates
+            = new ArrayList<>(List.of(Display.STATE_DOZE_SUSPEND));
 
     @Before
     public void setUp() throws Exception {
@@ -134,7 +148,7 @@
         mInjector = new Injector();
         when(mSurfaceControlProxy.getBootDisplayModeSupport()).thenReturn(true);
         mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler,
-                mListener, mInjector);
+                mListener, mFlags, mInjector);
         spyOn(mAdapter);
         doReturn(mMockedContext).when(mAdapter).getOverlayContext();
 
@@ -185,6 +199,8 @@
         when(mMockedResources.getIntArray(
             com.android.internal.R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
             .thenReturn(new int[]{});
+        doReturn(true).when(mFlags).isDisplayOffloadEnabled();
+        initDisplayOffloadSession();
     }
 
     @After
@@ -1109,6 +1125,72 @@
         assertThat(info.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP).isEqualTo(0);
     }
 
+
+    @Test
+    public void test_displayStateToSupportedState_DisplayOffloadStart()
+            throws InterruptedException {
+        // prepare a display.
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+        for (Integer supportedState : mDisplayOffloadSupportedStates) {
+            Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(
+                    supportedState, 0, 0, mDisplayOffloadSession);
+            changeStateRunnable.run();
+
+            verify(mDisplayOffloader).startOffload();
+        }
+    }
+
+    @Test
+    public void test_displayStateToDozeFromDozeSuspend_DisplayOffloadStop()
+            throws InterruptedException {
+        // prepare a display.
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+        Runnable changeStateToDozeSuspendRunnable = displayDevice.requestDisplayStateLocked(
+                Display.STATE_DOZE_SUSPEND, 0, 0, mDisplayOffloadSession);
+        Runnable changeStateToDozeRunnable = displayDevice.requestDisplayStateLocked(
+                Display.STATE_DOZE, 0, 0, mDisplayOffloadSession);
+        changeStateToDozeSuspendRunnable.run();
+        changeStateToDozeRunnable.run();
+
+        verify(mDisplayOffloader).stopOffload();
+    }
+
+    private void initDisplayOffloadSession() {
+        mDisplayOffloader = spy(new DisplayOffloader() {
+            @Override
+            public boolean startOffload() {
+                return true;
+            }
+
+            @Override
+            public void stopOffload() {}
+        });
+
+        mDisplayOffloadSession = new DisplayOffloadSession() {
+            @Override
+            public void setDozeStateOverride(int displayState) {}
+
+            @Override
+            public DisplayOffloader getDisplayOffloader() {
+                return mDisplayOffloader;
+            }
+        };
+    }
+
     private void setupCutoutAndRoundedCorners() {
         String sampleCutout = "M 507,66\n"
                 + "a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
index 37d966d..c3322ec 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/HdrClamperTest.java
@@ -20,6 +20,12 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
 import android.os.PowerManager;
 
 import androidx.test.filters.SmallTest;
@@ -31,6 +37,7 @@
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -40,7 +47,20 @@
 @SmallTest
 public class HdrClamperTest {
 
-    public static final float FLOAT_TOLERANCE = 0.0001f;
+    private static final float FLOAT_TOLERANCE = 0.0001f;
+    private static final long SEND_TIME_TOLERANCE = 100;
+
+    private static final HdrBrightnessData TEST_HDR_DATA = new HdrBrightnessData(
+            Map.of(500f, 0.6f),
+            /* brightnessIncreaseDebounceMillis= */ 1000,
+            /* brightnessIncreaseDurationMillis= */ 2000,
+            /* brightnessDecreaseDebounceMillis= */ 3000,
+            /* brightnessDecreaseDurationMillis= */4000
+    );
+
+    private static final int WIDTH = 600;
+    private static final int HEIGHT = 800;
+    private static final float MIN_HDR_PERCENT = 0.5f;
 
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
@@ -48,17 +68,31 @@
     @Mock
     private BrightnessClamperController.ClamperChangeListener mMockListener;
 
+    @Mock
+    private IBinder mMockBinder;
+
+    @Mock
+    private HdrClamper.Injector mMockInjector;
+
+    @Mock
+    private HdrClamper.HdrLayerInfoListener mMockHdrInfoListener;
+
     OffsettableClock mClock = new OffsettableClock.Stopped();
 
     private final TestHandler mTestHandler = new TestHandler(null, mClock);
 
 
     private HdrClamper mHdrClamper;
-
+    private HdrClamper.HdrListener mHdrChangeListener;
 
     @Before
     public void setUp() {
-        mHdrClamper = new HdrClamper(mMockListener, mTestHandler);
+        when(mMockInjector.getHdrListener(any(), any())).thenReturn(mMockHdrInfoListener);
+        mHdrClamper = new HdrClamper(mMockListener, mTestHandler, mMockInjector);
+        ArgumentCaptor<HdrClamper.HdrListener> listenerCaptor = ArgumentCaptor.forClass(
+                HdrClamper.HdrListener.class);
+        verify(mMockInjector).getHdrListener(listenerCaptor.capture(), eq(mTestHandler));
+        mHdrChangeListener = listenerCaptor.getValue();
         configureClamper();
     }
 
@@ -68,20 +102,23 @@
 
         assertFalse(mTestHandler.hasMessagesOrCallbacks());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
     }
 
     @Test
-    public void testClamper_AmbientLuxChangesBelowLimit() {
+    public void testClamper_AmbientLuxChangesBelowLimit_MaxDecrease() {
         mHdrClamper.onAmbientLuxChange(499);
 
         assertTrue(mTestHandler.hasMessagesOrCallbacks());
         TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
-        assertEquals(2000, msgInfo.sendTime);
+        assertSendTime(3000, msgInfo.sendTime);
         assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
 
-        mClock.fastForward(2000);
+        mClock.fastForward(3000);
         mTestHandler.timeAdvance();
         assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 4
     }
 
     @Test
@@ -91,33 +128,65 @@
 
         assertFalse(mTestHandler.hasMessagesOrCallbacks());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
     }
 
     @Test
     public void testClamper_AmbientLuxChangesBelowLimit_ThenSlowlyAboveLimit() {
         mHdrClamper.onAmbientLuxChange(499);
-        mClock.fastForward(2000);
+        mClock.fastForward(3000);
         mTestHandler.timeAdvance();
 
         mHdrClamper.onAmbientLuxChange(500);
 
         assertTrue(mTestHandler.hasMessagesOrCallbacks());
         TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
-        assertEquals(3000, msgInfo.sendTime); // 2000 + 1000
+        assertSendTime(4000, msgInfo.sendTime); // 3000 + 1000
 
         mClock.fastForward(1000);
         mTestHandler.timeAdvance();
         assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(0.2f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE); // (1-0.6) / 2
+    }
+
+    @Test
+    public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit() {
+        mHdrChangeListener.onHdrVisible(false);
+        mHdrClamper.onAmbientLuxChange(499);
+
+        assertFalse(mTestHandler.hasMessagesOrCallbacks());
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(-1, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+    }
+
+    @Test
+    public void testClamper_HdrOff_ThenAmbientLuxChangesBelowLimit_ThenHdrOn() {
+        mHdrChangeListener.onHdrVisible(false);
+        mHdrClamper.onAmbientLuxChange(499);
+        mHdrChangeListener.onHdrVisible(true);
+
+        assertTrue(mTestHandler.hasMessagesOrCallbacks());
+        TestHandler.MsgInfo msgInfo = mTestHandler.getPendingMessages().peek();
+        assertSendTime(3000, msgInfo.sendTime);
+        assertEquals(PowerManager.BRIGHTNESS_MAX, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+
+        mClock.fastForward(3000);
+        mTestHandler.timeAdvance();
+        assertEquals(0.6f, mHdrClamper.getMaxBrightness(), FLOAT_TOLERANCE);
+        assertEquals(0.1f, mHdrClamper.getTransitionRate(), FLOAT_TOLERANCE);
+    }
+
+    // MsgInfo.sendTime is calculated first by adding SystemClock.uptimeMillis()
+    // (in Handler.sendMessageDelayed) and then by subtracting SystemClock.uptimeMillis()
+    // (in TestHandler.sendMessageAtTime, there might be several milliseconds difference between
+    // SystemClock.uptimeMillis() calls, and subtracted value might be greater than added.
+    private static void assertSendTime(long expectedTime, long sendTime) {
+        assertTrue(expectedTime >= sendTime);
+        assertTrue(expectedTime - SEND_TIME_TOLERANCE < sendTime);
     }
 
     private void configureClamper() {
-        HdrBrightnessData data = new HdrBrightnessData(
-                Map.of(500f, 0.6f),
-                /* brightnessIncreaseDebounceMillis= */ 1000,
-                /* brightnessIncreaseDurationMillis= */ 1500,
-                /* brightnessDecreaseDebounceMillis= */ 2000,
-                /* brightnessDecreaseDurationMillis= */2500
-        );
-        mHdrClamper.resetHdrConfig(data);
+        mHdrClamper.resetHdrConfig(TEST_HDR_DATA, WIDTH, HEIGHT, MIN_HDR_PERCENT, mMockBinder);
+        mHdrChangeListener.onHdrVisible(true);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
index 880501f..f5c6bb2 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/state/DisplayStateControllerTest.java
@@ -141,6 +141,32 @@
         assertEquals(false, mDisplayStateController.shouldPerformScreenOffTransition());
     }
 
+    @Test
+    public void dozeScreenStateOverrideToDozeSuspend_DozePolicy_updateDisplayStateToDozeSuspend() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+        mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+        int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+                !DISPLAY_IN_TRANSITION);
+
+        assertEquals(state, Display.STATE_DOZE_SUSPEND);
+    }
+
+    @Test
+    public void dozeScreenStateOverrideToDozeSuspend_OffPolicy_displayRemainOff() {
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                new DisplayManagerInternal.DisplayPowerRequest();
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+        mDisplayStateController.overrideDozeScreenState(Display.STATE_DOZE_SUSPEND);
+
+        int state = mDisplayStateController.updateDisplayState(displayPowerRequest, DISPLAY_ENABLED,
+                !DISPLAY_IN_TRANSITION);
+
+        assertEquals(state, Display.STATE_OFF);
+    }
+
     private void validDisplayState(int policy, int displayState, boolean isEnabled,
             boolean isInTransition) {
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 52044bf..de8b308 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -117,7 +117,9 @@
                 Build.VERSION_CODES.CUR_DEVELOPMENT,
                 Build.VERSION.INCREMENTAL);
         mMockSystem.system().validateFinalState();
-        mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class));
+        mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class),
+                mock(RemovePackageHelper.class), mock(DeletePackageHelper.class),
+                mock(BroadcastHelper.class));
     }
 
     @NonNull
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
index d6a4d40..931b38d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
@@ -34,6 +34,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
 
 @RunWith(JUnit4::class)
 class DeletePackageHelperTest {
@@ -79,7 +80,8 @@
         whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
         whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, 1, 0, false)
 
         assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -97,7 +99,8 @@
         whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
             UserInfo(userId, "testparent", 0))
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, userId, 0, false)
 
         assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
@@ -112,7 +115,8 @@
         whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
             .thenReturn(PERMISSION_DENIED)
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, 1,
             PackageManager.DELETE_SYSTEM_APP, false)
 
@@ -133,7 +137,8 @@
         whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
             .thenReturn(PERMISSION_DENIED)
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, userId,
             PackageManager.DELETE_SYSTEM_APP, false)
 
@@ -150,7 +155,8 @@
         whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
             .thenReturn(PERMISSION_DENIED)
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, 1,
                 PackageManager.DELETE_SYSTEM_APP, false)
 
@@ -164,7 +170,8 @@
         whenever(mPms.checkPermission(CONTROL_KEYGUARD, "a.data.package", USER_SYSTEM))
             .thenReturn(PERMISSION_GRANTED)
 
-        val dph = DeletePackageHelper(mPms)
+        val dph = DeletePackageHelper(mPms, mock(RemovePackageHelper::class.java),
+            mock(BroadcastHelper::class.java))
         val result = dph.deletePackageX("a.data.package", 1L, 1,
             PackageManager.DELETE_SYSTEM_APP, false)
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
index 9f1cec3..cf81f0a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/DistractingPackageHelperTest.kt
@@ -39,7 +39,7 @@
     override fun setup() {
         super.setup()
         distractingPackageHelper = DistractingPackageHelper(
-                pms, rule.mocks().injector, broadcastHelper, suspendPackageHelper)
+                pms, broadcastHelper, suspendPackageHelper)
     }
 
     @Test
@@ -50,12 +50,11 @@
         testHandler.flush()
 
         verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
-                nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-                nullable(), nullable(), nullable(), nullable())
+        verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+                pkgListCaptor.capture(), any(), any(), flagsCaptor.capture())
 
-        val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+        val modifiedPackages = pkgListCaptor.value
+        val distractionFlags = flagsCaptor.value
         assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
         assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
         assertThat(unactionedPackages).isEmpty()
@@ -75,10 +74,8 @@
                 PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, TEST_USER_ID, deviceOwnerUid)
         testHandler.flush()
         verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper, never()).sendPackageBroadcast(
-                eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
-                nullable())
+        verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+                any(), any(), any(), any(), any())
         assertThat(unactionedPackages).isEmpty()
     }
 
@@ -154,11 +151,11 @@
         testHandler.flush()
 
         verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
-                nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-                nullable(), nullable(), nullable(), nullable())
-        val modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        val distractionFlags = bundleCaptor.value.getInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS)
+        verify(broadcastHelper).sendDistractingPackagesChanged(
+                any(Computer::class.java), pkgListCaptor.capture(), any(), eq(TEST_USER_ID),
+                flagsCaptor.capture())
+        val modifiedPackages = pkgListCaptor.value
+        val distractionFlags = flagsCaptor.value
         assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
         assertThat(distractionFlags).isEqualTo(PackageManager.RESTRICTION_NONE)
     }
@@ -170,9 +167,8 @@
         testHandler.flush()
 
         verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper, never()).sendPackageBroadcast(eq(
-                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
-                nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+        verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+                any(), any(), any(), any(), any())
     }
 
     @Test
@@ -189,22 +185,21 @@
                 arrayOfNulls(0), TEST_USER_ID)
         testHandler.flush()
         verify(pms, never()).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper, never()).sendPackageBroadcast(eq(
-                Intent.ACTION_DISTRACTING_PACKAGES_CHANGED), nullable(), nullable(), anyInt(),
-                nullable(), nullable(), any(), nullable(), nullable(), nullable(), nullable())
+        verify(broadcastHelper, never()).sendDistractingPackagesChanged(
+                any(), any(), any(), any(), any())
     }
 
     @Test
     fun sendDistractingPackagesChanged() {
-        distractingPackageHelper.sendDistractingPackagesChanged(packagesToChange, uidsToChange,
-                TEST_USER_ID, PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
+        broadcastHelper.sendDistractingPackagesChanged(pms.snapshotComputer(),
+                packagesToChange, uidsToChange, TEST_USER_ID,
+                PackageManager.RESTRICTION_HIDE_NOTIFICATIONS)
         testHandler.flush()
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED),
-                nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-                nullable(), nullable(), nullable(), nullable())
+        verify(broadcastHelper).sendDistractingPackagesChanged(any(Computer::class.java),
+                pkgListCaptor.capture(), uidsCaptor.capture(), eq(TEST_USER_ID), any())
 
-        var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
+        var changedPackages = pkgListCaptor.value
+        var changedUids = uidsCaptor.value
         assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
         assertThat(changedUids).asList().containsExactly(
                 packageSetting1.appId, packageSetting2.appId)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
index 5fd270e..eb00164 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt
@@ -17,7 +17,6 @@
 package com.android.server.pm
 
 import android.os.Build
-import android.os.Bundle
 import android.os.UserHandle
 import android.os.UserManager
 import com.android.server.pm.pkg.PackageStateInternal
@@ -68,7 +67,11 @@
     lateinit var protectedPackages: ProtectedPackages
 
     @Captor
-    lateinit var bundleCaptor: ArgumentCaptor<Bundle>
+    lateinit var pkgListCaptor: ArgumentCaptor<Array<String>>
+    @Captor
+    lateinit var flagsCaptor: ArgumentCaptor<Int>
+    @Captor
+    lateinit var uidsCaptor: ArgumentCaptor<IntArray>
 
     @Rule
     @JvmField
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index c5db5db..6c44fd0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -17,12 +17,12 @@
 package com.android.server.pm;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
@@ -63,9 +63,7 @@
 
     @Before
     public void setup() {
-        when(mMockSystem.mocks().getInjector().getHandler()).thenReturn(mHandler);
-        mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper(
-                mMockSystem.mocks().getInjector());
+        mPackageMonitorCallbackHelper = new PackageMonitorCallbackHelper();
     }
 
 
@@ -80,7 +78,7 @@
 
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -93,7 +91,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */,
-                null /* broadcastAllowList */);
+                null /* broadcastAllowList */, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
 
@@ -101,7 +99,7 @@
         mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -114,7 +112,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -138,7 +136,7 @@
         // Notify for user 10
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -155,7 +153,7 @@
         mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME,
                 false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */,
                 new int[]{0} /* userIds */, null /* instantUserIds */,
-                null /* broadcastAllowList */);
+                null /* broadcastAllowList */, mHandler);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -183,7 +181,8 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME,
                 FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0], false /* isArchived */,
-                PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */);
+                PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */,
+                mHandler);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -207,7 +206,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */,
                 true /* replacing */, new String[]{FAKE_PACKAGE_NAME},
-                new int[]{FAKE_PACKAGE_UID} /* uids */);
+                new int[]{FAKE_PACKAGE_UID} /* uids */, mHandler);
 
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(
@@ -240,7 +239,7 @@
         mPackageMonitorCallbackHelper.onUserRemoved(10);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */,
-                null /* instantUserIds */, null /* broadcastAllowList */);
+                null /* instantUserIds */, null /* broadcastAllowList */, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -256,7 +255,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList);
+                null /* instantUserIds */, broadcastAllowList, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
     }
@@ -272,7 +271,7 @@
                 Binder.getCallingUid());
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList);
+                null /* instantUserIds */, broadcastAllowList, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
@@ -288,7 +287,7 @@
                 Process.SYSTEM_UID);
         mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
                 FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
-                null /* instantUserIds */, broadcastAllowList);
+                null /* instantUserIds */, broadcastAllowList, mHandler);
 
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
index 6797576..4240373 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt
@@ -22,12 +22,11 @@
 import android.os.PersistableBundle
 import com.android.server.testutils.any
 import com.android.server.testutils.eq
-import com.android.server.testutils.nullable
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
@@ -44,17 +43,10 @@
         testHandler.flush()
 
         verify(pms).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_SUSPENDED),
-            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-            nullable(), nullable(), nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
-            nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(), nullable(),
-            nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_SUSPENDED), nullable(),
-            nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(), nullable(),
-            nullable(), nullable())
+        verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+            eq(Intent.ACTION_PACKAGES_SUSPENDED), pkgListCaptor.capture(), any(), any(), any())
 
-        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var modifiedPackages = pkgListCaptor.value
         assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
         assertThat(failedNames).isEmpty()
     }
@@ -146,6 +138,7 @@
                 null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE,
                 TEST_USER_ID, deviceOwnerUid, false /* forQuietMode */, false /* quarantined */)
         testHandler.flush()
+        Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
         failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(),
                 targetPackages, false /* suspended */, null /* appExtras */,
@@ -154,17 +147,13 @@
         testHandler.flush()
 
         verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
-            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-            nullable(), nullable(), nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
-            nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
-            nullable(), nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
-            nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
-            nullable(), nullable(), nullable())
+        verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+                eq(Intent.ACTION_PACKAGES_UNSUSPENDED), pkgListCaptor.capture(), any(), any(),
+                any())
+        verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+                any(), any(), any())
 
-        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
+        var modifiedPackages = pkgListCaptor.value
         assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
         assertThat(failedNames).isEmpty()
     }
@@ -206,7 +195,7 @@
         testHandler.flush()
         assertThat(failedNames).isEmpty()
 
-        val result = suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+        val result = SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)!!
 
         assertThat(result.getString(TEST_PACKAGE_1)).isEqualTo(TEST_PACKAGE_1)
@@ -222,14 +211,15 @@
                 null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid,
                 false /* forQuietMode */, false /* quarantined */)
         testHandler.flush()
+        Mockito.clearInvocations(broadcastHelper)
         assertThat(failedNames).isEmpty()
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isEqualTo(DEVICE_OWNER_PACKAGE)
-        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+        assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNotNull()
-        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+        assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNotNull()
 
         suspendPackageHelper.removeSuspensionsBySuspendingPackage(pms.snapshotComputer(),
@@ -238,23 +228,18 @@
 
         testHandler.flush()
         verify(pms, times(2)).scheduleWritePackageRestrictions(eq(TEST_USER_ID))
-        verify(broadcastHelper).sendPackageBroadcast(eq(Intent.ACTION_PACKAGES_UNSUSPENDED),
-            nullable(), bundleCaptor.capture(), anyInt(), nullable(), nullable(), any(),
-            nullable(), nullable(), nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
-            nullable(), nullable(), any(), eq(TEST_PACKAGE_1), nullable(), any(), any(),
-            nullable(), nullable(), nullable())
-        verify(broadcastHelper).doSendBroadcast(eq(Intent.ACTION_MY_PACKAGE_UNSUSPENDED),
-            nullable(), nullable(), any(), eq(TEST_PACKAGE_2), nullable(), any(), any(),
-            nullable(), nullable(), nullable())
+        verify(broadcastHelper).sendPackagesSuspendedOrUnsuspendedForUser(any(Computer::class.java),
+                eq(Intent.ACTION_PACKAGES_UNSUSPENDED), any(), any(), any(), any())
+        verify(broadcastHelper).sendMyPackageSuspendedOrUnsuspended(any(Computer::class.java),
+                any(), any(), any())
 
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
         assertThat(suspendPackageHelper.getSuspendingPackage(pms.snapshotComputer(),
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
-        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+        assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_1, TEST_USER_ID, deviceOwnerUid)).isNull()
-        assertThat(suspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
+        assertThat(SuspendPackageHelper.getSuspendedPackageAppExtras(pms.snapshotComputer(),
             TEST_PACKAGE_2, TEST_USER_ID, deviceOwnerUid)).isNull()
     }
 
@@ -319,39 +304,4 @@
 
         assertThat(result.title).isEqualTo(TEST_PACKAGE_1)
     }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendedForUser() {
-        suspendPackageHelper.sendPackagesSuspendedForUser(
-            Intent.ACTION_PACKAGES_SUSPENDED, packagesToChange, uidsToChange, false, TEST_USER_ID)
-        testHandler.flush()
-        verify(broadcastHelper).sendPackageBroadcast(any(), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
-                nullable())
-
-        var changedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        var changedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-        assertThat(changedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        assertThat(changedUids).asList().containsExactly(
-                packageSetting1.appId, packageSetting2.appId)
-    }
-
-    @Test
-    @Throws(Exception::class)
-    fun sendPackagesSuspendModifiedForUser() {
-        suspendPackageHelper.sendPackagesSuspendedForUser(
-            Intent.ACTION_PACKAGES_SUSPENSION_CHANGED, packagesToChange, uidsToChange, false, TEST_USER_ID)
-        testHandler.flush()
-        verify(broadcastHelper).sendPackageBroadcast(
-                eq(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED), nullable(), bundleCaptor.capture(),
-                anyInt(), nullable(), nullable(), any(), nullable(), nullable(), nullable(),
-                nullable())
-
-        var modifiedPackages = bundleCaptor.value.getStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST)
-        var modifiedUids = bundleCaptor.value.getIntArray(Intent.EXTRA_CHANGED_UID_LIST)
-        assertThat(modifiedPackages).asList().containsExactly(TEST_PACKAGE_1, TEST_PACKAGE_2)
-        assertThat(modifiedUids).asList().containsExactly(
-                packageSetting1.appId, packageSetting2.appId)
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 82b7540..2f0257a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -39,11 +39,16 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.hardware.display.DisplayManager;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
+import android.view.WindowManager;
 
 import com.android.server.accessibility.magnification.MagnificationProcessor;
 import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -76,6 +81,9 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     AccessibilityServiceConnection mConnection;
 
     @Mock AccessibilityUserState mMockUserState;
@@ -113,6 +121,8 @@
 
         when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
         when(mMockA11yTrace.isA11yTracingEnabled()).thenReturn(false);
+        when(mMockContext.getSystemService(Context.DISPLAY_SERVICE))
+                .thenReturn(new DisplayManager(mMockContext));
 
         mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
                 COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -168,6 +178,18 @@
         assertFalse(mConnection.getServiceInfo().crashed);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+    public void onServiceConnected_addsWindowTokens() {
+        setServiceBinding(COMPONENT_NAME);
+        mConnection.bindLocked();
+        mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+        verify(mMockWindowManagerInternal).addWindowToken(
+                any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+                anyInt(), eq(null));
+    }
+
     private void setServiceBinding(ComponentName componentName) {
         when(mMockUserState.getBindingServicesLocked())
                 .thenReturn(new HashSet<>(Arrays.asList(componentName)));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b4558b2..63281b7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -35,6 +35,7 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -46,6 +47,9 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.testing.DexmakerShareClassLoaderRule;
@@ -88,6 +92,9 @@
     @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock private AccessibilityServiceInfo mMockServiceInfo;
 
     @Mock private AccessibilityServiceConnection mMockConnection;
@@ -188,7 +195,7 @@
 
         mUserState.addServiceLocked(mMockConnection);
 
-        verify(mMockConnection, never()).onAdded();
+        verify(mMockListener, never()).onServiceInfoChangedLocked(any());
     }
 
     @Test
@@ -197,13 +204,24 @@
 
         mUserState.addServiceLocked(mMockConnection);
 
-        verify(mMockConnection).onAdded();
         assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
         assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
         verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
     }
 
     @Test
+    // addServiceLocked only calls addWindowTokensForAllDisplays when
+    // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
+    @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+    public void addService_flagDisabled_addsWindowTokens() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+        mUserState.addServiceLocked(mMockConnection);
+
+        verify(mMockConnection).addWindowTokensForAllDisplays();
+    }
+
+    @Test
     public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
         // When soft kb show mode is hidden in settings and is auto in state.
         putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 4ce9ba0..3ee5f61 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -21,8 +21,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+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;
 
@@ -36,6 +39,10 @@
 import android.content.pm.ServiceInfo;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.server.accessibility.test.MessageCapturingHandler;
@@ -43,6 +50,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -58,6 +66,9 @@
 
     MessageCapturingHandler mMessageCapturingHandler;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
@@ -197,6 +208,24 @@
         assertEquals(0, mUiAutomationManager.getRequestedEventMaskLocked());
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
+    public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
+        register(0);
+        // registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
+        verify(mMockWindowManagerInternal, never()).addWindowToken(
+                any(), anyInt(), anyInt(), any());
+
+        // Advance UiAutomationManager#UiAutomationService's handler.
+        mMessageCapturingHandler.sendAllMessages();
+
+        // After advancing the handler we expect addWindowToken to have been called
+        // by the UiAutomationService instance.
+        verify(mMockWindowManagerInternal).addWindowToken(
+                any(), eq(WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY),
+                anyInt(), eq(null));
+    }
+
     private void register(int flags) {
         mUiAutomationManager.registerUiTestAutomationServiceLocked(mMockOwner,
                 mMockAccessibilityServiceClient, mMockContext, mMockServiceInfo, SERVICE_ID,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index a0bca3b..24ad976 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -50,7 +50,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.platform.test.annotations.FlakyTest;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
 import android.view.InputDevice;
@@ -60,6 +59,7 @@
 import android.view.accessibility.MagnificationAnimationCallback;
 
 import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.FlakyTest;
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index dee7780..37a6d22 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -781,7 +781,7 @@
                 password, PRIMARY_USER_ID, 0 /* flags */).getResponseCode());
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
 
-        mService.onCleanupUser(PRIMARY_USER_ID);
+        mService.onUserStopped(PRIMARY_USER_ID);
         assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
 
         assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
index 2c9ba34..e8b7ad7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/WeakEscrowTokenTests.java
@@ -169,7 +169,7 @@
         assertTrue(mService.isWeakEscrowTokenActive(handle, PRIMARY_USER_ID));
         assertTrue(mService.isWeakEscrowTokenValid(handle, token, PRIMARY_USER_ID));
 
-        mService.onCleanupUser(PRIMARY_USER_ID);
+        mService.onUserStopped(PRIMARY_USER_ID);
         assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
 
         assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 020afdb..8d7b5cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -141,6 +141,7 @@
 import com.android.server.notification.PermissionHelper.PackagePermission;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.json.JSONArray;
@@ -563,7 +564,7 @@
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId(), false));
 
         List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+                PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
         boolean foundNcg = false;
         for (NotificationChannelGroup actual : actualGroups) {
             if (ncg.getId().equals(actual.getId())) {
@@ -647,7 +648,7 @@
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3.getId(), false));
 
         List<NotificationChannelGroup> actualGroups = mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, false, true, false).getList();
+                PKG_N_MR1, UID_N_MR1, false, true, false, true, null).getList();
         boolean foundNcg = false;
         for (NotificationChannelGroup actual : actualGroups) {
             if (ncg.getId().equals(actual.getId())) {
@@ -2620,6 +2621,16 @@
     }
 
     @Test
+    public void testOnlyHasDefaultChannel() throws Exception {
+        assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+                UID_N_MR1, false);
+        assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+    }
+
+    @Test
     public void testCreateDeletedChannel() throws Exception {
         long[] vibration = new long[]{100, 67, 145, 156};
         NotificationChannel channel =
@@ -2644,16 +2655,6 @@
     }
 
     @Test
-    public void testOnlyHasDefaultChannel() throws Exception {
-        assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
-        assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
-
-        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
-                UID_N_MR1, false);
-        assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
-    }
-
-    @Test
     public void testCreateChannel_defaultChannelId() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, new NotificationChannel(
@@ -2884,7 +2885,7 @@
                 UID_N_MR1});
 
         assertEquals(0, mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, true, true, false).getList().size());
+                PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList().size());
     }
 
     @Test
@@ -3022,7 +3023,7 @@
                 UID_N_MR1, false);
 
         List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+                PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
         assertEquals(3, actual.size());
         for (NotificationChannelGroup group : actual) {
             if (group.getId() == null) {
@@ -3056,14 +3057,15 @@
         channel1.setGroup(ncg.getId());
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
                 UID_N_MR1, false);
-        mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+        mHelper.getNotificationChannelGroups(PKG_N_MR1, UID_N_MR1, true, true, false, true, null)
+                .getList();
 
         channel1.setImportance(IMPORTANCE_LOW);
         mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true,
                 UID_N_MR1, false);
 
         List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, true, true, false).getList();
+                PKG_N_MR1, UID_N_MR1, true, true, false, true, null).getList();
 
         assertEquals(2, actual.size());
         for (NotificationChannelGroup group : actual) {
@@ -3089,7 +3091,7 @@
                 UID_N_MR1, false);
 
         List<NotificationChannelGroup> actual = mHelper.getNotificationChannelGroups(
-                PKG_N_MR1, UID_N_MR1, false, false, true).getList();
+                PKG_N_MR1, UID_N_MR1, false, false, true, true, null).getList();
 
         assertEquals(2, actual.size());
         for (NotificationChannelGroup group : actual) {
@@ -5786,6 +5788,62 @@
         assertFalse(isUserSet);
     }
 
+    @Test
+    public void testGetNotificationChannelGroups_withChannelFilter_includeBlocked() {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+                UID_N_MR1, false);
+        // modifying same object, don't need to call updateNotificationChannel
+        channel.setImportance(IMPORTANCE_NONE);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+                UID_N_MR1, false);
+
+        NotificationChannel channel3 =
+                new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+                UID_N_MR1, false);
+
+        Set<String> filter = ImmutableSet.of("id3");
+
+        NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+                PKG_N_MR1, UID_N_MR1, false, true, false, true, filter).getList().get(0);
+        assertEquals(2, actual.getChannels().size());
+        assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+        assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+    }
+
+    @Test
+    public void testGetNotificationChannelGroups_withChannelFilter_doNotIncludeBlocked() {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+                UID_N_MR1, false);
+        // modifying same object, don't need to call updateNotificationChannel
+        channel.setImportance(IMPORTANCE_NONE);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false,
+                UID_N_MR1, false);
+
+        NotificationChannel channel3 =
+                new NotificationChannel("id3", "name3", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, true, false,
+                UID_N_MR1, false);
+
+        Set<String> filter = ImmutableSet.of("id3");
+
+        NotificationChannelGroup actual = mHelper.getNotificationChannelGroups(
+                PKG_N_MR1, UID_N_MR1, false, true, false, false, filter).getList().get(0);
+        assertEquals(1, actual.getChannels().size());
+        assertEquals(1, actual.getChannels().stream().filter(c -> c.getId().equals("id3")).count());
+        assertEquals(0, actual.getChannels().stream().filter(c -> c.getId().equals("id2")).count());
+    }
+
     private static NotificationChannel cloneChannel(NotificationChannel original) {
         Parcel parcel = Parcel.obtain();
         try {
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 61c4d06..8db09f9 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -46,6 +46,7 @@
 import static java.util.Collections.unmodifiableMap;
 
 import android.content.Context;
+import android.os.Looper;
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.view.InputDevice;
@@ -98,6 +99,10 @@
      *      settings values.
      */
     protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+
         doReturn(mSettingsProviderRule.mockContentResolver(mContext))
                 .when(mContext).getContentResolver();
         mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 6e2c4bd..ef28ffa 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -70,6 +70,7 @@
 import android.hardware.input.InputManager;
 import android.media.AudioManagerInternal;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
@@ -77,7 +78,6 @@
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.os.VibratorInfo;
-import android.os.test.TestLooper;
 import android.service.dreams.DreamManagerInternal;
 import android.telecom.TelecomManager;
 import android.util.FeatureFlagUtils;
@@ -160,8 +160,8 @@
     @Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
 
     private StaticMockitoSession mMockitoSession;
+    private HandlerThread mHandlerThread;
     private Handler mHandler;
-    private TestLooper mTestLooper;
 
     private class TestInjector extends PhoneWindowManager.Injector {
         TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
@@ -184,11 +184,12 @@
 
     TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
         MockitoAnnotations.initMocks(this);
-        mTestLooper = new TestLooper();
-        mHandler = new Handler(mTestLooper.getLooper());
+        mHandlerThread = new HandlerThread("fake window manager");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
         mContext = mockingDetails(context).isSpy() ? context : spy(context);
-        mHandler.post(() -> setUp(supportSettingsUpdate));
-        mTestLooper.dispatchAll();
+        mHandler.runWithScissors(() -> setUp(supportSettingsUpdate),  0 /* timeout */);
+        waitForIdle();
     }
 
     private void setUp(boolean supportSettingsUpdate) {
@@ -300,6 +301,7 @@
     }
 
     void tearDown() {
+        mHandlerThread.quitSafely();
         LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
         Mockito.reset(mPhoneWindowManager);
         mMockitoSession.finishMocking();
@@ -326,7 +328,7 @@
     }
 
     void waitForIdle() {
-        mTestLooper.dispatchAll();
+        mHandler.runWithScissors(() -> { }, 0 /* timeout */);
     }
 
     /**
diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp
new file mode 100644
index 0000000..78b0ede
--- /dev/null
+++ b/tests/BinderLeakTest/Android.bp
@@ -0,0 +1,40 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "binder_leak_test_aidl",
+    srcs: ["**/*.aidl"],
+    path: "aidl",
+}
+
+java_defaults {
+    name: "BinderTest.defaults",
+    srcs: [
+        "**/*.java",
+        ":binder_leak_test_aidl",
+    ],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "androidx.test.runner",
+    ],
+}
+
+// Built with target_sdk_version: current
+android_test {
+    name: "BinderLeakTest",
+    defaults: ["BinderTest.defaults"],
+}
+
+// Built with target_sdk_version: 33
+android_test {
+    name: "BinderLeakTest_legacy",
+    defaults: ["BinderTest.defaults"],
+    manifest: "AndroidManifest_legacy.xml",
+}
diff --git a/tests/BinderLeakTest/AndroidManifest.xml b/tests/BinderLeakTest/AndroidManifest.xml
new file mode 100644
index 0000000..756def7
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.test.binder">
+    <application>
+        <service
+            android:name=".MyService"
+            android:enabled="true"
+            android:exported="true"
+            android:process=":service">
+        </service>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.test.binder"
+        android:label="Binder leak test">
+    </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/AndroidManifest_legacy.xml b/tests/BinderLeakTest/AndroidManifest_legacy.xml
new file mode 100644
index 0000000..03d1dfd
--- /dev/null
+++ b/tests/BinderLeakTest/AndroidManifest_legacy.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.test.binder">
+    <uses-sdk android:minSdkVersion="33"
+          android:targetSdkVersion="33"
+          android:maxSdkVersion="33" />
+    <application>
+        <service
+            android:name=".MyService"
+            android:enabled="true"
+            android:exported="true"
+            android:process=":service">
+        </service>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.test.binder"
+        android:label="Binder leak test">
+    </instrumentation>
+</manifest>
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
new file mode 100644
index 0000000..a721959
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFoo.aidl
@@ -0,0 +1,5 @@
+package com.android.test.binder;
+
+interface IFoo {
+
+}
diff --git a/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
new file mode 100644
index 0000000..b487f51
--- /dev/null
+++ b/tests/BinderLeakTest/aidl/com/android/test/binder/IFooProvider.aidl
@@ -0,0 +1,10 @@
+package com.android.test.binder;
+import com.android.test.binder.IFoo;
+
+interface IFooProvider {
+    IFoo createFoo();
+
+    boolean isFooGarbageCollected();
+
+    oneway void killProcess();
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
new file mode 100644
index 0000000..f07317f
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/BinderTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.test.binder;
+
+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 android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ServiceTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.TimeoutException;
+
+@RunWith(AndroidJUnit4.class)
+public class BinderTest {
+    @Rule
+    public final ServiceTestRule serviceRule = new ServiceTestRule();
+
+    @Test
+    public void testDeathRecipientLeaksOrNot()
+            throws RemoteException, TimeoutException, InterruptedException {
+        Intent intent = new Intent(ApplicationProvider.getApplicationContext(), MyService.class);
+        IFooProvider provider = IFooProvider.Stub.asInterface(serviceRule.bindService(intent));
+        FooHolder holder = new FooHolder(provider.createFoo());
+
+        // ref will get enqueued right after holder is finalized for gc.
+        ReferenceQueue<FooHolder> refQueue = new ReferenceQueue<>();
+        PhantomReference<FooHolder> ref = new PhantomReference<>(holder, refQueue);
+
+        DeathRecorder deathRecorder = new DeathRecorder();
+        holder.registerDeathRecorder(deathRecorder);
+
+        if (getSdkVersion() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            /////////////////////////////////////////////
+            // New behavior
+            //
+            // Reference chain at this moment:
+            // holder --(java strong ref)--> FooHolder
+            // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+            // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+            // BinderProxy --(binder ref)--> Foo.Stub
+            // In other words, the variable "holder" is the root of the reference chain.
+
+            // By setting the variable to null, we make FooHolder, IFoo.Proxy, BinderProxy, and even
+            // Foo.Stub unreachable.
+            holder = null;
+
+            // Ensure that the objects are garbage collected
+            forceGc();
+            assertEquals(ref, refQueue.poll());
+            assertTrue(provider.isFooGarbageCollected());
+
+            // The binder has died, but we don't get notified since the death recipient is GC'ed.
+            provider.killProcess();
+            Thread.sleep(1000); // give some time for the service process to die and reaped
+            assertFalse(deathRecorder.deathRecorded);
+        } else {
+            /////////////////////////////////////////////
+            // Legacy behavior
+            //
+            // Reference chain at this moment:
+            // JavaDeathRecipient --(JNI strong ref)--> FooHolder
+            // holder --(java strong ref)--> FooHolder
+            // FooHolder.mProxy --(java strong ref)--> IFoo.Proxy
+            // IFoo.Proxy.mRemote --(java strong ref)--> BinderProxy
+            // BinderProxy --(binder ref)--> Foo.Stub
+            // So, BOTH JavaDeathRecipient and holder are roots of the reference chain.
+
+            // Even if we set holder to null, it doesn't make other objects unreachable; they are
+            // still reachable via the JNI strong ref.
+            holder = null;
+
+            // Check that objects are not garbage collected
+            forceGc();
+            assertNotEquals(ref, refQueue.poll());
+            assertFalse(provider.isFooGarbageCollected());
+
+            // The legacy behavior is getting notified even when there's no reference
+            provider.killProcess();
+            Thread.sleep(1000); // give some time for the service process to die and reaped
+            assertTrue(deathRecorder.deathRecorded);
+        }
+    }
+
+    static class FooHolder implements IBinder.DeathRecipient {
+        private IFoo mProxy;
+        private DeathRecorder mDeathRecorder;
+
+        FooHolder(IFoo proxy) throws RemoteException {
+            proxy.asBinder().linkToDeath(this, 0);
+
+            // A strong reference from DeathRecipient(this) to the binder proxy is created here
+            mProxy = proxy;
+        }
+
+        public void registerDeathRecorder(DeathRecorder dr) {
+            mDeathRecorder = dr;
+        }
+
+        @Override
+        public void binderDied() {
+            if (mDeathRecorder != null) {
+                mDeathRecorder.deathRecorded = true;
+            }
+        }
+    }
+
+    static class DeathRecorder {
+        public boolean deathRecorded = false;
+    }
+
+    // Try calling System.gc() until an orphaned object is confirmed to be finalized
+    private static void forceGc() {
+        Object obj = new Object();
+        ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+        PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+        obj = null; // make it an orphan
+        while (refQueue.poll() != ref) {
+            System.gc();
+        }
+    }
+
+    private static int getSdkVersion() {
+        return ApplicationProvider.getApplicationContext().getApplicationInfo().targetSdkVersion;
+    }
+}
diff --git a/tests/BinderLeakTest/java/com/android/test/binder/MyService.java b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
new file mode 100644
index 0000000..c701253
--- /dev/null
+++ b/tests/BinderLeakTest/java/com/android/test/binder/MyService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open 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.test.binder;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+
+public class MyService extends Service {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new IFooProvider.Stub() {
+            ReferenceQueue<IFoo> mRefQueue = new ReferenceQueue<>();
+            PhantomReference<IFoo> mRef;
+
+            @Override
+            public IFoo createFoo() throws RemoteException {
+                IFoo binder = new IFoo.Stub() {};
+                mRef = new PhantomReference<>(binder, mRefQueue);
+                return binder;
+            }
+
+            @Override
+            public boolean isFooGarbageCollected() throws RemoteException {
+                forceGc();
+                return mRefQueue.poll() == mRef;
+            }
+
+            @Override
+            public void killProcess() throws RemoteException {
+                android.os.Process.killProcess(android.os.Process.myPid());
+            }
+        };
+    }
+
+    private static void forceGc() {
+        Object obj = new Object();
+        ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
+        PhantomReference<Object> ref = new PhantomReference<>(obj, refQueue);
+        obj = null; // make it an orphan
+        while (refQueue.poll() != ref) {
+            System.gc();
+        }
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 1aa4859..0c1d88a 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -13,11 +13,11 @@
 }
 SourceFile: "HostSideTestClassLoadHook.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -33,11 +33,11 @@
 }
 SourceFile: "HostSideTestKeep.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -56,11 +56,11 @@
 }
 SourceFile: "HostSideTestNativeSubstitutionClass.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -76,11 +76,11 @@
 }
 SourceFile: "HostSideTestRemove.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -96,11 +96,11 @@
 }
 SourceFile: "HostSideTestStub.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -119,11 +119,11 @@
 }
 SourceFile: "HostSideTestSubstitute.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.METHOD]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -139,11 +139,11 @@
 }
 SourceFile: "HostSideTestThrow.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -159,11 +159,11 @@
 }
 SourceFile: "HostSideTestWholeClassKeep.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -179,11 +179,11 @@
 }
 SourceFile: "HostSideTestWholeClassStub.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  1: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -199,7 +199,7 @@
 }
 SourceFile: "HostSideTestSuppress.java"
 RuntimeVisibleAnnotations:
-  0: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD]
     )
@@ -217,9 +217,9 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -230,11 +230,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: iconst_1
-         1: ireturn
+         x: iconst_1
+         x: ireturn
       LineNumberTable:
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   public static int getOneStub();
@@ -242,11 +242,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: iconst_1
-         1: ireturn
+         x: iconst_1
+         x: ireturn
       LineNumberTable:
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 SourceFile: "TinyFrameworkCallerCheck.java"
@@ -267,9 +267,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -280,8 +280,8 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: invokestatic  #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
-         3: ireturn
+         x: invokestatic  #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+         x: ireturn
       LineNumberTable:
 
   public static int getOne_noCheck();
@@ -289,13 +289,13 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
-         3: ireturn
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+         x: ireturn
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkCallerCheck.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -314,14 +314,14 @@
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   public int remove;
@@ -333,21 +333,21 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                  // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                  // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addOne(int);
@@ -355,17 +355,17 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
             0       6     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addOneInner(int);
@@ -373,17 +373,17 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_1
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
             0       4     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   public void toBeRemoved(java.lang.String);
@@ -391,17 +391,17 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         7: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
             0       8     1   foo   Ljava/lang/String;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestRemove
 
   public int addTwo(int);
@@ -409,20 +409,20 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String not supported on host side
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String not supported on host side
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
             0      10     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
-      1: #x(#x=s#x)
+      x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
@@ -432,10 +432,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -446,9 +446,9 @@
     descriptor: (I)I
     flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
-      1: #x(#x=s#x)
+      x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
@@ -458,10 +458,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -472,14 +472,14 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         2: areturn
+         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestThrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
@@ -487,22 +487,22 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 SourceFile: "TinyFrameworkClassAnnotations.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
@@ -532,15 +532,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                  // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                  // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -551,10 +551,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -566,10 +566,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_1
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -581,10 +581,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         7: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -596,20 +596,20 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String not supported on host side
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String not supported on host side
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
             0      10     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
-      1: #x(#x=s#x)
+      x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
@@ -619,10 +619,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -633,9 +633,9 @@
     descriptor: (I)I
     flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
-      1: #x(#x=s#x)
+      x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
@@ -645,10 +645,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -659,8 +659,8 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         2: areturn
+         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -671,9 +671,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -681,7 +681,7 @@
 }
 SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
   Compiled from "TinyFrameworkClassLoadHook.java"
@@ -702,9 +702,9 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -715,11 +715,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: getstatic     #x                  // Field sLoadedClasses:Ljava/util/Set;
-         3: aload_0
-         4: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
-         9: pop
-        10: return
+         x: getstatic     #x                  // Field sLoadedClasses:Ljava/util/Set;
+         x: aload_0
+         x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+         x: pop
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -734,16 +734,16 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class java/util/HashSet
-         3: dup
-         4: invokespecial #x                 // Method java/util/HashSet."<init>":()V
-         7: putstatic     #x                  // Field sLoadedClasses:Ljava/util/Set;
-        10: return
+         x: new           #x                 // class java/util/HashSet
+         x: dup
+         x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
+         x: putstatic     #x                  // Field sLoadedClasses:Ljava/util/Set;
+        x: return
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkClassLoadHook.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
   Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -763,9 +763,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -776,18 +776,18 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: iconst_1
-         1: putstatic     #x                  // Field sInitialized:Z
-         4: return
+         x: iconst_1
+         x: putstatic     #x                  // Field sInitialized:Z
+         x: return
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkClassWithInitializer.java"
 RuntimeInvisibleAnnotations:
-  0: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
-  1: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
@@ -803,9 +803,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -816,18 +816,18 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=1, args_size=0
-         0: new           #x                  // class java/lang/IllegalStateException
-         3: dup
-         4: ldc           #x                  // String Inner exception
-         6: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
-         9: athrow
-        10: astore_0
-        11: new           #x                 // class java/lang/RuntimeException
-        14: dup
-        15: ldc           #x                 // String Outer exception
-        17: aload_0
-        18: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
-        21: athrow
+         x: new           #x                  // class java/lang/IllegalStateException
+         x: dup
+         x: ldc           #x                  // String Inner exception
+         x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+         x: athrow
+        x: astore_0
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Outer exception
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+        x: athrow
       Exception table:
          from    to  target type
              0    10    10   Class java/lang/Exception
@@ -841,7 +841,7 @@
 }
 SourceFile: "TinyFrameworkExceptionTester.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
   Compiled from "TinyFrameworkForTextPolicy.java"
@@ -869,15 +869,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                  // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                  // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -888,10 +888,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -903,10 +903,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_1
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -918,10 +918,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         7: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -933,11 +933,11 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String not supported on host side
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String not supported on host side
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -949,10 +949,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -968,10 +968,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -982,8 +982,8 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         2: areturn
+         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -994,9 +994,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1017,9 +1017,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1034,9 +1034,9 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iload_0
-         1: invokestatic  #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-         4: ireturn
+         x: iload_0
+         x: invokestatic  #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1051,10 +1051,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=4, args_size=2
-         0: lload_0
-         1: lload_2
-         2: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-         5: lreturn
+         x: lload_0
+         x: lload_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+         x: lreturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1063,9 +1063,9 @@
 }
 SourceFile: "TinyFrameworkNative.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
       value="TinyFrameworkNative_host"
     )
@@ -1083,9 +1083,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1096,10 +1096,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1110,10 +1110,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=4, args_size=2
-         0: lload_0
-         1: lload_2
-         2: ladd
-         3: lreturn
+         x: lload_0
+         x: lload_2
+         x: ladd
+         x: lreturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1122,7 +1122,7 @@
 }
 SourceFile: "TinyFrameworkNative_host.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
   Compiled from "TinyFrameworkNestedClasses.java"
@@ -1142,12 +1142,12 @@
     flags: (0x0000)
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         9: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1159,9 +1159,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iconst_1
-         1: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         4: areturn
+         x: iconst_1
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1172,9 +1172,9 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1200,9 +1200,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1213,9 +1213,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iconst_2
-         1: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         4: areturn
+         x: iconst_2
+         x: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1226,9 +1226,9 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1258,12 +1258,12 @@
     flags: (0x0000)
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         9: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1275,9 +1275,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iconst_3
-         1: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         4: areturn
+         x: iconst_3
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1288,9 +1288,9 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1316,9 +1316,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1329,9 +1329,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iconst_4
-         1: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         4: areturn
+         x: iconst_4
+         x: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1342,9 +1342,9 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1374,12 +1374,12 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iload_1
-         6: putfield      #x                  // Field value:I
-         9: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                  // Field value:I
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1412,15 +1412,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         9: aload_0
-        10: iconst_5
-        11: putfield      #x                 // Field value:I
-        14: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                  // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+        x: iconst_5
+        x: putfield      #x                 // Field value:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1429,7 +1429,7 @@
 }
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 InnerClasses:
@@ -1448,9 +1448,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1461,9 +1461,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: bipush        7
-         2: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         5: areturn
+         x: bipush        7
+         x: invokestatic  #x                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1474,9 +1474,9 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1507,12 +1507,12 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: bipush        6
-         7: putfield      #x                  // Field value:I
-        10: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: bipush        6
+         x: putfield      #x                  // Field value:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1523,16 +1523,16 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
-         7: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+         x: areturn
       LineNumberTable:
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 }
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 InnerClasses:
@@ -1552,10 +1552,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
-         5: return
+         x: aload_0
+         x: iload_1
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1591,15 +1591,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         8: dup
-         9: aload_0
-        10: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-        13: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        16: return
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: dup
+         x: aload_0
+        x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1610,11 +1610,11 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         3: dup
-         4: aload_0
-         5: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         8: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: dup
+         x: aload_0
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1626,10 +1626,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
-         7: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+         x: areturn
       LineNumberTable:
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 
@@ -1638,16 +1638,16 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
-         7: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-        10: return
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index 6e1528a..43ceec4 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -12,33 +12,33 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int getOneStub();
     descriptor: ()I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 InnerClasses:
   private static #x= #x of #x;           // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 SourceFile: "TinyFrameworkCallerCheck.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -55,44 +55,44 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int getOne_withCheck();
     descriptor: ()I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int getOne_noCheck();
     descriptor: ()I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 InnerClasses:
   private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 SourceFile: "TinyFrameworkCallerCheck.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -109,7 +109,7 @@
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
@@ -117,13 +117,13 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addOne(int);
@@ -131,13 +131,13 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addTwo(int);
@@ -145,47 +145,47 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int nativeAddThree(int);
     descriptor: (I)I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
     descriptor: ()Ljava/lang/String;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 SourceFile: "TinyFrameworkClassAnnotations.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
@@ -215,97 +215,97 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public int addOne(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public int addOneInner(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public void toBeRemoved(java.lang.String);
     descriptor: (Ljava/lang/String;)V
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public int addTwo(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int nativeAddThree(int);
     descriptor: (I)I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public java.lang.String unsupportedMethod();
     descriptor: ()Ljava/lang/String;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
     descriptor: ()Ljava/lang/String;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
   Compiled from "TinyFrameworkClassLoadHook.java"
@@ -326,22 +326,22 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static void onClassLoaded(java.lang.Class<?>);
     descriptor: (Ljava/lang/Class;)V
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     Signature: #x                          // (Ljava/lang/Class<*>;)V
 
   static {};
@@ -349,20 +349,20 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkClassLoadHook.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
   Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -382,35 +382,35 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   static {};
     descriptor: ()V
     flags: (0x0008) ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkClassWithInitializer.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
-  1: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
@@ -426,31 +426,31 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int testException();
     descriptor: ()I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkExceptionTester.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
   Compiled from "TinyFrameworkForTextPolicy.java"
@@ -470,61 +470,61 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public int addOne(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public int addTwo(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static int nativeAddThree(int);
     descriptor: (I)I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
     descriptor: ()Ljava/lang/String;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkForTextPolicy.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
@@ -540,11 +540,11 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static native int nativeAddTwo(int);
     descriptor: (I)I
@@ -555,11 +555,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static native long nativeLongPlus(long, long);
     descriptor: (JJ)J
@@ -570,22 +570,22 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=4, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 SourceFile: "TinyFrameworkNative.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
       value="TinyFrameworkNative_host"
     )
@@ -607,19 +607,19 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 InnerClasses:
   public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -644,22 +644,22 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 InnerClasses:
   public #x= #x of #x;                   // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -680,22 +680,22 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
     descriptor: ()Ljava/util/function/Supplier;
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 }
 InnerClasses:
@@ -703,12 +703,12 @@
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -725,20 +725,20 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 InnerClasses:
   public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -765,22 +765,22 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 
   public java.util.function.Supplier<java.lang.Integer> getSupplier();
     descriptor: ()Ljava/util/function/Supplier;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 
   public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
@@ -788,11 +788,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 
   static {};
@@ -800,11 +800,11 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=3, locals=0, args_size=0
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: ldc           #x                 // String Stub!
-         6: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         9: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
 }
 InnerClasses:
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
@@ -818,12 +818,12 @@
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 5672e9c..faf0a46 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -13,13 +13,13 @@
 }
 SourceFile: "HostSideTestClassLoadHook.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -35,13 +35,13 @@
 }
 SourceFile: "HostSideTestKeep.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -60,13 +60,13 @@
 }
 SourceFile: "HostSideTestNativeSubstitutionClass.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -82,13 +82,13 @@
 }
 SourceFile: "HostSideTestRemove.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -104,13 +104,13 @@
 }
 SourceFile: "HostSideTestStub.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -129,13 +129,13 @@
 }
 SourceFile: "HostSideTestSubstitute.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.METHOD]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -151,13 +151,13 @@
 }
 SourceFile: "HostSideTestThrow.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x,e#x.#x])
+  x: #x(#x=[e#x.#x,e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -173,13 +173,13 @@
 }
 SourceFile: "HostSideTestWholeClassKeep.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -195,13 +195,13 @@
 }
 SourceFile: "HostSideTestWholeClassStub.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
-  1: #x(#x=[e#x.#x])
+  x: #x(#x=[e#x.#x])
     java.lang.annotation.Target(
       value=[Ljava/lang/annotation/ElementType;.TYPE]
     )
-  2: #x(#x=e#x.#x)
+  x: #x(#x=e#x.#x)
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
@@ -219,9 +219,9 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -232,17 +232,17 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=0, args_size=0
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         2: ldc           #x                 // String getOneKeep
-         4: ldc           #x                 // String ()I
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iconst_1
-        16: ireturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
+         x: ldc           #x                 // String getOneKeep
+         x: ldc           #x                 // String ()I
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iconst_1
+        x: ireturn
       LineNumberTable:
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   public static int getOneStub();
@@ -250,20 +250,20 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: iconst_1
-         1: ireturn
+         x: iconst_1
+         x: ireturn
       LineNumberTable:
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 InnerClasses:
   private static #x= #x of #x;           // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 SourceFile: "TinyFrameworkCallerCheck.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
@@ -280,9 +280,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -293,8 +293,8 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
-         3: ireturn
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
+         x: ireturn
       LineNumberTable:
 
   public static int getOne_noCheck();
@@ -302,20 +302,20 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=0, args_size=0
-         0: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
-         3: ireturn
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
+         x: ireturn
       LineNumberTable:
 }
 InnerClasses:
   private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
 SourceFile: "TinyFrameworkCallerCheck.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
@@ -332,14 +332,14 @@
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   private static {};
@@ -347,31 +347,31 @@
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         2: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         4: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         7: return
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                 // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addOne(int);
@@ -379,17 +379,17 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
             0       6     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 
   public int addOneInner(int);
@@ -397,23 +397,23 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=2, args_size=2
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         2: ldc           #x                 // String addOneInner
-         4: ldc           #x                 // String (I)I
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iload_1
-        16: iconst_1
-        17: iadd
-        18: ireturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+         x: ldc           #x                 // String addOneInner
+         x: ldc           #x                 // String (I)I
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iload_1
+        x: iconst_1
+        x: iadd
+        x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
            15       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
            15       4     1 value   I
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
   public int addTwo(int);
@@ -421,10 +421,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -436,10 +436,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -450,20 +450,20 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         2: ldc           #x                 // String unsupportedMethod
-         4: ldc           #x                 // String ()Ljava/lang/String;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        18: new           #x                 // class java/lang/RuntimeException
-        21: dup
-        22: ldc           #x                 // String Unreachable
-        24: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        27: athrow
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
+         x: ldc           #x                 // String unsupportedMethod
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestThrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
@@ -471,27 +471,27 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
     RuntimeInvisibleAnnotations:
-      0: #x()
+      x: #x()
         android.hosttest.annotation.HostSideTestStub
 }
 SourceFile: "TinyFrameworkClassAnnotations.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
@@ -521,15 +521,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                 // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -540,10 +540,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -555,10 +555,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_1
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -570,10 +570,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: new           #x                 // class java/lang/RuntimeException
-         3: dup
-         4: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         7: athrow
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -585,10 +585,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -600,10 +600,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -614,8 +614,8 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         2: areturn
+         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -626,9 +626,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -636,12 +636,12 @@
 }
 SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
   Compiled from "TinyFrameworkClassLoadHook.java"
@@ -662,9 +662,9 @@
     flags: (0x0002) ACC_PRIVATE
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -675,11 +675,11 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-         3: aload_0
-         4: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
-         9: pop
-        10: return
+         x: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+         x: aload_0
+         x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+         x: pop
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -694,21 +694,21 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class java/util/HashSet
-         3: dup
-         4: invokespecial #x                 // Method java/util/HashSet."<init>":()V
-         7: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-        10: return
+         x: new           #x                 // class java/util/HashSet
+         x: dup
+         x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
+         x: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+        x: return
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkClassLoadHook.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer.class
   Compiled from "TinyFrameworkClassWithInitializer.java"
@@ -728,9 +728,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -741,26 +741,26 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
-         2: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         4: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         7: iconst_1
-         8: putstatic     #x                 // Field sInitialized:Z
-        11: return
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializer
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: iconst_1
+         x: putstatic     #x                 // Field sInitialized:Z
+        x: return
       LineNumberTable:
 }
 SourceFile: "TinyFrameworkClassWithInitializer.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
-  1: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
@@ -776,9 +776,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -789,18 +789,18 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=1, args_size=0
-         0: new           #x                 // class java/lang/IllegalStateException
-         3: dup
-         4: ldc           #x                 // String Inner exception
-         6: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
-         9: athrow
-        10: astore_0
-        11: new           #x                 // class java/lang/RuntimeException
-        14: dup
-        15: ldc           #x                 // String Outer exception
-        17: aload_0
-        18: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
-        21: athrow
+         x: new           #x                 // class java/lang/IllegalStateException
+         x: dup
+         x: ldc           #x                 // String Inner exception
+         x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+         x: athrow
+        x: astore_0
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Outer exception
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+        x: athrow
       Exception table:
          from    to  target type
              0    10    10   Class java/lang/Exception
@@ -814,12 +814,12 @@
 }
 SourceFile: "TinyFrameworkExceptionTester.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
   Compiled from "TinyFrameworkForTextPolicy.java"
@@ -843,25 +843,25 @@
     flags: (0x000a) ACC_PRIVATE, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         2: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         4: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         7: return
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iconst_1
-         6: putfield      #x                 // Field stub:I
-         9: aload_0
-        10: iconst_2
-        11: putfield      #x                 // Field keep:I
-        14: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field stub:I
+         x: aload_0
+        x: iconst_2
+        x: putfield      #x                 // Field keep:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -872,10 +872,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokevirtual #x                 // Method addOneInner:(I)I
-         5: ireturn
+         x: aload_0
+         x: iload_1
+         x: invokevirtual #x                 // Method addOneInner:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -887,16 +887,16 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=2, args_size=2
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         2: ldc           #x                 // String addOneInner
-         4: ldc           #x                 // String (I)I
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iload_1
-        16: iconst_1
-        17: iadd
-        18: ireturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String addOneInner
+         x: ldc           #x                 // String (I)I
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iload_1
+        x: iconst_1
+        x: iadd
+        x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -908,10 +908,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: iload_1
-         1: iconst_2
-         2: iadd
-         3: ireturn
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -923,10 +923,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=1, args_size=1
-         0: iload_0
-         1: iconst_3
-         2: iadd
-         3: ireturn
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -937,27 +937,27 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         2: ldc           #x                 // String unsupportedMethod
-         4: ldc           #x                 // String ()Ljava/lang/String;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        18: new           #x                 // class java/lang/RuntimeException
-        21: dup
-        22: ldc           #x                 // String Unreachable
-        24: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        27: athrow
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String unsupportedMethod
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
 
   public java.lang.String visibleButUsesUnsupportedMethod();
     descriptor: ()Ljava/lang/String;
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         4: areturn
+         x: aload_0
+         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -965,9 +965,9 @@
 }
 SourceFile: "TinyFrameworkForTextPolicy.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
@@ -983,9 +983,9 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -996,18 +996,18 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iload_0
-         1: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-         4: ireturn
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+         x: ireturn
 
   public static int nativeAddTwo_should_be_like_this(int);
     descriptor: (I)I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=1, args_size=1
-         0: iload_0
-         1: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-         4: ireturn
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+         x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1018,20 +1018,20 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=4, args_size=2
-         0: lload_0
-         1: lload_2
-         2: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-         5: lreturn
+         x: lload_0
+         x: lload_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+         x: lreturn
 
   public static long nativeLongPlus_should_be_like_this(long, long);
     descriptor: (JJ)J
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=4, args_size=2
-         0: lload_0
-         1: lload_2
-         2: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-         5: lreturn
+         x: lload_0
+         x: lload_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+         x: lreturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1040,14 +1040,14 @@
 }
 SourceFile: "TinyFrameworkNative.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
-  1: #x(#x=s#x)
+  x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
       value="TinyFrameworkNative_host"
     )
@@ -1065,15 +1065,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         2: ldc           #x                 // String <init>
-         4: ldc           #x                 // String ()V
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        19: return
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1084,16 +1084,16 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         2: ldc           #x                 // String nativeAddTwo
-         4: ldc           #x                 // String (I)I
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iload_0
-        16: iconst_2
-        17: iadd
-        18: ireturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeAddTwo
+         x: ldc           #x                 // String (I)I
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iload_0
+        x: iconst_2
+        x: iadd
+        x: ireturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1104,16 +1104,16 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=4, locals=4, args_size=2
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         2: ldc           #x                 // String nativeLongPlus
-         4: ldc           #x                 // String (JJ)J
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: lload_0
-        16: lload_2
-        17: ladd
-        18: lreturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeLongPlus
+         x: ldc           #x                 // String (JJ)J
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: lload_0
+        x: lload_2
+        x: ladd
+        x: lreturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1122,10 +1122,10 @@
 }
 SourceFile: "TinyFrameworkNative_host.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
   Compiled from "TinyFrameworkNestedClasses.java"
@@ -1145,12 +1145,12 @@
     flags: (0x0000)
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         9: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1162,15 +1162,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Integer;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iconst_1
-        16: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iconst_1
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1181,15 +1181,15 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Object;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1201,7 +1201,7 @@
 Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
@@ -1218,9 +1218,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1231,15 +1231,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Integer;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iconst_2
-        16: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iconst_2
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1250,15 +1250,15 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Object;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1270,7 +1270,7 @@
 Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
@@ -1291,12 +1291,12 @@
     flags: (0x0000)
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         9: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1308,15 +1308,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Integer;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iconst_3
-        16: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iconst_3
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1327,15 +1327,15 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Object;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1347,7 +1347,7 @@
 Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
@@ -1364,9 +1364,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1377,15 +1377,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Integer;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: iconst_4
-        16: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: iconst_4
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1396,15 +1396,15 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Object;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1416,7 +1416,7 @@
 Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
@@ -1437,12 +1437,12 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: iload_1
-         6: putfield      #x                 // Field value:I
-         9: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field value:I
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1453,9 +1453,9 @@
   public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
@@ -1480,15 +1480,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: aload_1
-         2: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         5: aload_0
-         6: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         9: aload_0
-        10: iconst_5
-        11: putfield      #x                 // Field value:I
-        14: return
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+        x: iconst_5
+        x: putfield      #x                 // Field value:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1499,12 +1499,12 @@
   public #x= #x of #x;                   // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
@@ -1521,9 +1521,9 @@
     flags: (0x0000)
     Code:
       stack=1, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1534,15 +1534,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Integer;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: bipush        7
-        17: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        20: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: bipush        7
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1553,15 +1553,15 @@
     flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
     Code:
       stack=4, locals=1, args_size=1
-         0: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         2: ldc           #x                 // String get
-         4: ldc           #x                 // String ()Ljava/lang/Object;
-         6: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         9: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        12: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        15: aload_0
-        16: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        19: areturn
+         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
+         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1574,7 +1574,7 @@
 Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
@@ -1595,12 +1595,12 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: bipush        6
-         7: putfield      #x                 // Field value:I
-        10: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: bipush        6
+         x: putfield      #x                 // Field value:I
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1611,10 +1611,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
-         7: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+         x: areturn
       LineNumberTable:
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 }
@@ -1623,12 +1623,12 @@
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
@@ -1645,10 +1645,10 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         0: aload_0
-         1: iload_1
-         2: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
-         5: return
+         x: aload_0
+         x: iload_1
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1660,9 +1660,9 @@
   public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
@@ -1689,15 +1689,15 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=4, locals=1, args_size=1
-         0: aload_0
-         1: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         4: aload_0
-         5: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         8: dup
-         9: aload_0
-        10: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-        13: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        16: return
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: dup
+         x: aload_0
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1708,11 +1708,11 @@
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=3, locals=1, args_size=1
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         3: dup
-         4: aload_0
-         5: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         8: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: dup
+         x: aload_0
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: areturn
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
@@ -1724,10 +1724,10 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
-         7: areturn
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+         x: areturn
       LineNumberTable:
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 
@@ -1736,11 +1736,11 @@
     flags: (0x0008) ACC_STATIC
     Code:
       stack=2, locals=0, args_size=0
-         0: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         3: dup
-         4: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
-         7: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-        10: return
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
       LineNumberTable:
 }
 InnerClasses:
@@ -1755,12 +1755,12 @@
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeVisibleAnnotations:
-  0: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
-  1: #x()
+  x: #x()
     com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
 RuntimeInvisibleAnnotations:
-  0: #x()
+  x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index 93729fb..992665e 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -93,6 +93,7 @@
   if (( $simple )) ; then
     # For "simple output" mode,
     # - Normalize the constant numbers (replace with "#x")
+    # - Normalize byte code offsets and other similar numbers. (e.g. "0:" -> "x:")
     # - Remove the constant pool
     # - Remove the line number table
     # - Some other transient lines
@@ -100,6 +101,7 @@
     # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
     # the start and the end lines.
     sed -e 's/#[0-9][0-9]*/#x/g' \
+        -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
         -e '/^Constant pool:/,/^[^ ]/{//!d}' \
         -e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
         -e '/SHA-256 checksum/d' \
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
index 4a2e37e..2e82beb 100644
--- a/tools/lint/fix/soong_lint_fix.py
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -14,6 +14,7 @@
 
 import argparse
 import json
+import functools
 import os
 import shutil
 import subprocess
@@ -28,6 +29,7 @@
 PATH_PREFIX = "out/soong/.intermediates"
 PATH_SUFFIX = "android_common/lint"
 FIX_ZIP = "suggested-fixes.zip"
+MODULE_JAVA_DEPS = "out/soong/module_bp_java_deps.json"
 
 
 class SoongModule:
@@ -49,11 +51,26 @@
         print(f"Found module {partial_path}/{self._name}.")
         self._path = f"{PATH_PREFIX}/{partial_path}/{self._name}/{PATH_SUFFIX}"
 
+    def find_java_deps(self, module_java_deps):
+        """Finds the dependencies of a Java module in the loaded module_bp_java_deps.json.
+
+        Returns:
+            A list of module names.
+        """
+        if self._name not in module_java_deps:
+            raise Exception(f"Module {self._name} not found!")
+
+        return module_java_deps[self._name]["dependencies"]
+
     @property
     def name(self):
         return self._name
 
     @property
+    def path(self):
+        return self._path
+
+    @property
     def lint_report(self):
         return f"{self._path}/lint-report.txt"
 
@@ -62,52 +79,25 @@
         return f"{self._path}/{FIX_ZIP}"
 
 
-class SoongLintFix:
+class SoongLintWrapper:
     """
-    This class creates a command line tool that will apply lint fixes to the
-    platform via the necessary combination of soong and shell commands.
+    This class wraps the necessary calls to Soong and/or shell commands to lint
+    platform modules and apply suggested fixes if desired.
 
-    It breaks up these operations into a few "private" methods that are
-    intentionally exposed so experimental code can tweak behavior.
-
-    The entry point, `run`, will apply lint fixes using the intermediate
-    `suggested-fixes` directory that soong creates during its invocation of
-    lint.
-
-    Basic usage:
-    ```
-    from soong_lint_fix import SoongLintFix
-
-    opts = SoongLintFixOptions()
-    opts.parse_args(sys.argv)
-    SoongLintFix(opts).run()
-    ```
+    It breaks up these operations into a few methods that are available to
+    sub-classes (see SoongLintFix for an example).
     """
-    def __init__(self, opts):
-        self._opts = opts
+    def __init__(self, check=None, lint_module=None):
+        self._check = check
+        self._lint_module = lint_module
         self._kwargs = None
-        self._modules = []
-
-    def run(self):
-        """
-        Run the script
-        """
-        self._setup()
-        self._find_modules()
-        self._lint()
-
-        if not self._opts.no_fix:
-            self._fix()
-
-        if self._opts.print:
-            self._print()
 
     def _setup(self):
         env = os.environ.copy()
-        if self._opts.check:
-            env["ANDROID_LINT_CHECK"] = self._opts.check
-        if self._opts.lint_module:
-            env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._opts.lint_module
+        if self._check:
+            env["ANDROID_LINT_CHECK"] = self._check
+        if self._lint_module:
+            env["ANDROID_LINT_CHECK_EXTRA_MODULES"] = self._lint_module
 
         self._kwargs = {
             "env": env,
@@ -117,7 +107,10 @@
 
         os.chdir(ANDROID_BUILD_TOP)
 
-        print("Refreshing soong modules...")
+    @functools.cached_property
+    def _module_info(self):
+        """Returns the JSON content of module-info.json."""
+        print("Refreshing Soong modules...")
         try:
             os.mkdir(ANDROID_PRODUCT_OUT)
         except OSError:
@@ -125,19 +118,54 @@
         subprocess.call(f"{SOONG_UI} --make-mode {PRODUCT_OUT}/module-info.json", **self._kwargs)
         print("done.")
 
-
-    def _find_modules(self):
         with open(f"{ANDROID_PRODUCT_OUT}/module-info.json") as f:
-            module_info = json.load(f)
+            return json.load(f)
 
-        for module_name in self._opts.modules:
-            module = SoongModule(module_name)
-            module.find(module_info)
-            self._modules.append(module)
+    def _find_module(self, module_name):
+        """Returns a SoongModule from a module name.
 
-    def _lint(self):
+        Ensures that the module is known to Soong.
+        """
+        module = SoongModule(module_name)
+        module.find(self._module_info)
+        return module
+
+    def _find_modules(self, module_names):
+        modules = []
+        for module_name in module_names:
+            modules.append(self._find_module(module_name))
+        return modules
+
+    @functools.cached_property
+    def _module_java_deps(self):
+        """Returns the JSON content of module_bp_java_deps.json."""
+        print("Refreshing Soong Java deps...")
+        subprocess.call(f"{SOONG_UI} --make-mode {MODULE_JAVA_DEPS}", **self._kwargs)
+        print("done.")
+
+        with open(f"{MODULE_JAVA_DEPS}") as f:
+            return json.load(f)
+
+    def _find_module_java_deps(self, module):
+        """Returns a list a dependencies for a module.
+
+        Args:
+            module: A SoongModule.
+
+        Returns:
+            A list of SoongModule.
+        """
+        deps = []
+        dep_names = module.find_java_deps(self._module_java_deps)
+        for dep_name in dep_names:
+            dep = SoongModule(dep_name)
+            dep.find(self._module_info)
+            deps.append(dep)
+        return deps
+
+    def _lint(self, modules):
         print("Cleaning up any old lint results...")
-        for module in self._modules:
+        for module in modules:
             try:
                 os.remove(f"{module.lint_report}")
                 os.remove(f"{module.suggested_fixes}")
@@ -145,13 +173,13 @@
                 pass
         print("done.")
 
-        target = " ".join([ module.lint_report for module in self._modules ])
+        target = " ".join([ module.lint_report for module in modules ])
         print(f"Generating {target}")
         subprocess.call(f"{SOONG_UI} --make-mode {target}", **self._kwargs)
         print("done.")
 
-    def _fix(self):
-        for module in self._modules:
+    def _fix(self, modules):
+        for module in modules:
             print(f"Copying suggested fixes for {module.name} to the tree...")
             with zipfile.ZipFile(f"{module.suggested_fixes}") as zip:
                 for name in zip.namelist():
@@ -161,13 +189,40 @@
                         shutil.copyfileobj(src, dst)
             print("done.")
 
-    def _print(self):
-        for module in self._modules:
+    def _print(self, modules):
+        for module in modules:
             print(f"### lint-report.txt {module.name} ###", end="\n\n")
             with open(module.lint_report, "r") as f:
                 print(f.read())
 
 
+class SoongLintFix(SoongLintWrapper):
+    """
+    Basic usage:
+    ```
+    from soong_lint_fix import SoongLintFix
+
+    opts = SoongLintFixOptions()
+    opts.parse_args()
+    SoongLintFix(opts).run()
+    ```
+    """
+    def __init__(self, opts):
+        super().__init__(check=opts.check, lint_module=opts.lint_module)
+        self._opts = opts
+
+    def run(self):
+        self._setup()
+        modules = self._find_modules(self._opts.modules)
+        self._lint(modules)
+
+        if not self._opts.no_fix:
+            self._fix(modules)
+
+        if self._opts.print:
+            self._print(modules)
+
+
 class SoongLintFixOptions:
     """Options for SoongLintFix"""
 
diff --git a/tools/lint/utils/enforce_permission_counter.py b/tools/lint/utils/enforce_permission_counter.py
index b5c2ffe..a4c00f7 100644
--- a/tools/lint/utils/enforce_permission_counter.py
+++ b/tools/lint/utils/enforce_permission_counter.py
@@ -16,57 +16,38 @@
 
 import soong_lint_fix
 
-# Libraries that constitute system_server.
-# It is non-trivial to keep in sync with services/Android.bp as some
-# module are post-processed (e.g, services.core).
-TARGETS = [
-        "services.core.unboosted",
-        "services.accessibility",
-        "services.appprediction",
-        "services.appwidget",
-        "services.autofill",
-        "services.backup",
-        "services.companion",
-        "services.contentcapture",
-        "services.contentsuggestions",
-        "services.coverage",
-        "services.devicepolicy",
-        "services.midi",
-        "services.musicsearch",
-        "services.net",
-        "services.people",
-        "services.print",
-        "services.profcollect",
-        "services.restrictions",
-        "services.searchui",
-        "services.smartspace",
-        "services.systemcaptions",
-        "services.translation",
-        "services.texttospeech",
-        "services.usage",
-        "services.usb",
-        "services.voiceinteraction",
-        "services.wallpapereffectsgeneration",
-        "services.wifi",
-]
+CHECK = "AnnotatedAidlCounter"
+LINT_MODULE = "AndroidUtilsLintChecker"
 
-
-class EnforcePermissionMigratedCounter:
+class EnforcePermissionMigratedCounter(soong_lint_fix.SoongLintWrapper):
     """Wrapper around lint_fix to count the number of AIDL methods annotated."""
+
+    def __init__(self):
+        super().__init__(check=CHECK, lint_module=LINT_MODULE)
+
     def run(self):
-        opts = soong_lint_fix.SoongLintFixOptions()
-        opts.check = "AnnotatedAidlCounter"
-        opts.lint_module = "AndroidUtilsLintChecker"
-        opts.no_fix = True
-        opts.modules = TARGETS
+        self._setup()
 
-        self.linter = soong_lint_fix.SoongLintFix(opts)
-        self.linter.run()
-        self.parse_lint_reports()
+        # Analyze the dependencies of the "services" module and the module
+        # "services.core.unboosted".
+        service_module = self._find_module("services")
+        dep_modules = self._find_module_java_deps(service_module) + \
+                      [self._find_module("services.core.unboosted")]
 
-    def parse_lint_reports(self):
+        # Skip dependencies that are not services. Skip the "services.core"
+        # module which is analyzed via "services.core.unboosted".
+        modules = []
+        for module in dep_modules:
+            if "frameworks/base/services" not in module.path:
+                continue
+            if module.name == "services.core":
+                continue
+            modules.append(module)
+
+        self._lint(modules)
+
         counts = { "unannotated": 0, "enforced": 0, "notRequired": 0 }
-        for module in self.linter._modules:
+        for module in modules:
             with open(module.lint_report, "r") as f:
                 content = f.read()
                 keys = dict(re.findall(r'(\w+)=(\d+)', content))