Merge "Remove unnecessary letter spaceing from the left and right of line" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2ae72ef..217101e7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -13,70 +13,142 @@
 // limitations under the License.
 
 aconfig_srcjars = [
-    ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+    // !!! KEEP THIS LIST ALPHABETICAL !!!
+    ":aconfig_mediacodec_flags_java_lib{.generated_srcjars}",
+    ":android.app.flags-aconfig-java{.generated_srcjars}",
     ":android.app.smartspace.flags-aconfig-java{.generated_srcjars}",
+    ":android.app.usage.flags-aconfig-java{.generated_srcjars}",
+    ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
+    ":android.chre.flags-aconfig-java{.generated_srcjars}",
     ":android.companion.flags-aconfig-java{.generated_srcjars}",
+    ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
+    ":android.content.flags-aconfig-java{.generated_srcjars}",
     ":android.content.pm.flags-aconfig-java{.generated_srcjars}",
     ":android.content.res.flags-aconfig-java{.generated_srcjars}",
+    ":android.credentials.flags-aconfig-java{.generated_srcjars}",
+    ":android.database.sqlite-aconfig-java{.generated_srcjars}",
+    ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
     ":android.hardware.flags-aconfig-java{.generated_srcjars}",
     ":android.hardware.radio.flags-aconfig-java{.generated_srcjars}",
+    ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
     ":android.location.flags-aconfig-java{.generated_srcjars}",
+    ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
+    ":android.multiuser.flags-aconfig-java{.generated_srcjars}",
     ":android.net.vcn.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}",
+    ":android.permission.flags-aconfig-java{.generated_srcjars}",
+    ":android.provider.flags-aconfig-java{.generated_srcjars}",
     ":android.security.flags-aconfig-java{.generated_srcjars}",
     ":android.server.app.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
     ":android.service.chooser.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.controls.flags-aconfig-java{.generated_srcjars}",
     ":android.service.dreams.flags-aconfig-java{.generated_srcjars}",
     ":android.service.notification.flags-aconfig-java{.generated_srcjars}",
-    ":android.view.flags-aconfig-java{.generated_srcjars}",
+    ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
+    ":android.speech.flags-aconfig-java{.generated_srcjars}",
+    ":android.tracing.flags-aconfig-java{.generated_srcjars}",
     ":android.view.accessibility.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.flags-aconfig-java{.generated_srcjars}",
+    ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
+    ":android.webkit.flags-aconfig-java{.generated_srcjars}",
+    ":android.widget.flags-aconfig-java{.generated_srcjars}",
     ":audio-framework-aconfig",
     ":camera_platform_flags_core_java_lib{.generated_srcjars}",
-    ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
-    ":android.hardware.biometrics.flags-aconfig-java{.generated_srcjars}",
     ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
     ":com.android.input.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
+    ":com.android.net.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
     ":com.android.text.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+    ":device_policy_aconfig_flags_lib{.generated_srcjars}",
+    ":display_flags_lib{.generated_srcjars}",
     ":framework-jobscheduler-job.flags-aconfig-java{.generated_srcjars}",
+    ":framework_graphics_flags_java_lib{.generated_srcjars}",
+    ":hwui_flags_java_lib{.generated_srcjars}",
+    ":power_flags_lib{.generated_srcjars}",
+    ":sdk_sandbox_flags_lib{.generated_srcjars}",
+    ":surfaceflinger_flags_java_lib{.generated_srcjars}",
     ":telecom_flags_core_java_lib{.generated_srcjars}",
     ":telephony_flags_core_java_lib{.generated_srcjars}",
-    ":android.companion.virtual.flags-aconfig-java{.generated_srcjars}",
-    ":android.view.inputmethod.flags-aconfig-java{.generated_srcjars}",
-    ":android.widget.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
-    ":sdk_sandbox_flags_lib{.generated_srcjars}",
-    ":android.permission.flags-aconfig-java{.generated_srcjars}",
-    ":android.database.sqlite-aconfig-java{.generated_srcjars}",
-    ":hwui_flags_java_lib{.generated_srcjars}",
-    ":framework_graphics_flags_java_lib{.generated_srcjars}",
-    ":display_flags_lib{.generated_srcjars}",
-    ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
-    ":android.multiuser.flags-aconfig-java{.generated_srcjars}",
-    ":android.app.flags-aconfig-java{.generated_srcjars}",
-    ":android.credentials.flags-aconfig-java{.generated_srcjars}",
-    ":android.view.contentprotection.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.server.flags.services-aconfig-java{.generated_srcjars}",
-    ":android.service.controls.flags-aconfig-java{.generated_srcjars}",
-    ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
-    ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
-    ":android.service.autofill.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.net.flags-aconfig-java{.generated_srcjars}",
-    ":device_policy_aconfig_flags_lib{.generated_srcjars}",
-    ":surfaceflinger_flags_java_lib{.generated_srcjars}",
-    ":android.view.contentcapture.flags-aconfig-java{.generated_srcjars}",
-    ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}",
-    ":android.tracing.flags-aconfig-java{.generated_srcjars}",
-    ":android.appwidget.flags-aconfig-java{.generated_srcjars}",
-    ":android.webkit.flags-aconfig-java{.generated_srcjars}",
-    ":android.provider.flags-aconfig-java{.generated_srcjars}",
-    ":android.chre.flags-aconfig-java{.generated_srcjars}",
-    ":android.speech.flags-aconfig-java{.generated_srcjars}",
-    ":power_flags_lib{.generated_srcjars}",
-    ":android.content.flags-aconfig-java{.generated_srcjars}",
+    // !!! KEEP THIS LIST ALPHABETICAL !!!
 ]
 
+stubs_defaults {
+    name: "framework-minus-apex-aconfig-declarations",
+    aconfig_declarations: [
+        "android.app.flags-aconfig",
+        "android.app.smartspace.flags-aconfig",
+        "android.app.usage.flags-aconfig",
+        "android.appwidget.flags-aconfig",
+        "android.companion.flags-aconfig",
+        "android.companion.virtual.flags-aconfig",
+        "android.content.pm.flags-aconfig",
+        "android.content.res.flags-aconfig",
+        "android.credentials.flags-aconfig",
+        "android.database.sqlite-aconfig",
+        "android.hardware.biometrics.flags-aconfig",
+        "android.hardware.flags-aconfig",
+        "android.hardware.radio.flags-aconfig",
+        "android.hardware.usb.flags-aconfig",
+        "android.location.flags-aconfig",
+        "android.media.audio-aconfig",
+        "android.media.audiopolicy-aconfig",
+        "android.media.midi-aconfig",
+        "android.media.tv.flags-aconfig",
+        "android.multiuser.flags-aconfig",
+        "android.net.vcn.flags-aconfig",
+        "android.nfc.flags-aconfig",
+        "android.os.flags-aconfig",
+        "android.os.vibrator.flags-aconfig",
+        "android.permission.flags-aconfig",
+        "android.provider.flags-aconfig",
+        "android.security.flags-aconfig",
+        "android.server.app.flags-aconfig",
+        "android.service.autofill.flags-aconfig",
+        "android.service.chooser.flags-aconfig",
+        "android.service.controls.flags-aconfig",
+        "android.service.dreams.flags-aconfig",
+        "android.service.notification.flags-aconfig",
+        "android.service.voice.flags-aconfig",
+        "android.speech.flags-aconfig",
+        "android.tracing.flags-aconfig",
+        "android.view.accessibility.flags-aconfig",
+        "android.view.contentcapture.flags-aconfig",
+        "android.view.contentprotection.flags-aconfig",
+        "android.view.flags-aconfig",
+        "android.view.inputmethod.flags-aconfig",
+        "android.webkit.flags-aconfig",
+        "android.widget.flags-aconfig",
+        "camera_platform_flags",
+        "chre_flags",
+        "com.android.hardware.input.input-aconfig",
+        "com.android.input.flags-aconfig",
+        "com.android.media.flags.bettertogether-aconfig",
+        "com.android.net.flags-aconfig",
+        "com.android.server.flags.services-aconfig",
+        "com.android.text.flags-aconfig",
+        "com.android.window.flags.window-aconfig",
+        "device_policy_aconfig_flags",
+        "display_flags",
+        "fold_lock_setting_flags",
+        "framework-jobscheduler-job.flags-aconfig",
+        "framework_graphics_flags",
+        "hwui_flags",
+        "power_flags",
+        "sdk_sandbox_flags",
+        "surfaceflinger_flags",
+        "telecom_flags",
+        "telephony_flags",
+    ],
+}
+
 filegroup {
     name: "framework-minus-apex-aconfig-srcjars",
     srcs: aconfig_srcjars,
diff --git a/Ravenwood.bp b/Ravenwood.bp
index d13c4d7..0877bce 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -97,6 +97,7 @@
         "framework-minus-apex.ravenwood",
         "hoststubgen-helper-runtime.ravenwood",
         "hoststubgen-helper-framework-runtime.ravenwood",
+        "core-libart-for-host",
         "all-updatable-modules-system-stubs",
         "junit",
         "truth",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d59775f..c904eb4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -138,15 +138,13 @@
     }
   ],
   "postsubmit-ravenwood": [
-    // TODO(ravenwood) promote it to presubmit
-    // TODO: Enable it once the infra knows how to run it.
-//    {
-//      "name": "CtsUtilTestCasesRavenwood",
-//      "file_patterns": [
-//        "*Ravenwood*",
-//        "*ravenwood*"
-//      ]
-//    }
+    {
+      "name": "CtsUtilTestCasesRavenwood",
+      "host": true,
+      "file_patterns": [
+        "[Rr]avenwood"
+      ]
+    }
   ],
   "postsubmit-managedprofile-stress": [
     {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 2c9af67..44afbe6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -951,7 +951,7 @@
         @VisibleForTesting
         static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
         @VisibleForTesting
-        static final long DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS = 72 * HOUR_IN_MILLIS;
+        static final long DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_MS = 24 * HOUR_IN_MILLIS;
         private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS;
         @VisibleForTesting
         final int[] DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS = {50, 60, 70, 80};
diff --git a/api/Android.bp b/api/Android.bp
index 1686943..126176d 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -269,6 +269,7 @@
 // classpath (or sources) somehow.
 stubs_defaults {
     name: "android-non-updatable-stubs-defaults",
+    defaults: ["framework-minus-apex-aconfig-declarations"],
     srcs: [":android-non-updatable-stub-sources"],
     sdk_version: "none",
     system_modules: "none",
diff --git a/core/api/current.txt b/core/api/current.txt
index 5fa0c66..d4cec2d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5317,7 +5317,6 @@
     ctor @Deprecated public AutomaticZenRule(String, android.content.ComponentName, android.net.Uri, int, boolean);
     ctor public AutomaticZenRule(@NonNull String, @Nullable android.content.ComponentName, @Nullable android.content.ComponentName, @NonNull android.net.Uri, @Nullable android.service.notification.ZenPolicy, int, boolean);
     ctor public AutomaticZenRule(android.os.Parcel);
-    method @FlaggedApi("android.app.modes_api") public boolean canUpdate();
     method public int describeContents();
     method public android.net.Uri getConditionId();
     method @Nullable public android.content.ComponentName getConfigurationActivity();
@@ -13676,11 +13675,8 @@
   @FlaggedApi("android.content.res.font_scale_converter_public") public interface FontScaleConverter {
     method public float convertDpToSp(float);
     method public float convertSpToDp(float);
-  }
-
-  @FlaggedApi("android.content.res.font_scale_converter_public") public class FontScaleConverterFactory {
-    method @FlaggedApi("android.content.res.font_scale_converter_public") @AnyThread @Nullable public static android.content.res.FontScaleConverter forScale(float);
-    method @FlaggedApi("android.content.res.font_scale_converter_public") @AnyThread public static boolean isNonLinearFontScalingActive(float);
+    method @AnyThread @Nullable public static android.content.res.FontScaleConverter forScale(float);
+    method @AnyThread public static boolean isNonLinearFontScalingActive(float);
   }
 
   public class ObbInfo implements android.os.Parcelable {
@@ -18685,6 +18681,8 @@
     method @Nullable public int getAllowedAuthenticators();
     method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
     method @Nullable public CharSequence getDescription();
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.graphics.Bitmap getLogoBitmap();
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public int getLogoRes();
     method @Nullable public CharSequence getNegativeButtonText();
     method @Nullable public CharSequence getSubtitle();
     method @NonNull public CharSequence getTitle();
@@ -18734,6 +18732,8 @@
     method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
     method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@DrawableRes int);
+    method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission("android.permission.MANAGE_BIOMETRIC_DIALOG") public android.hardware.biometrics.BiometricPrompt.Builder setLogo(@NonNull android.graphics.Bitmap);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
     method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -18755,21 +18755,21 @@
     method @Nullable public java.security.Signature getSignature();
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentListItem {
+  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentItem {
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentListItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentListItem {
-    ctor public PromptContentListItemBulletedText(@NonNull CharSequence);
+  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemBulletedText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+    ctor public PromptContentItemBulletedText(@NonNull CharSequence);
     method public int describeContents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentListItemBulletedText> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemBulletedText> CREATOR;
   }
 
-  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentListItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentListItem {
-    ctor public PromptContentListItemPlainText(@NonNull CharSequence);
+  @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentItemPlainText implements android.os.Parcelable android.hardware.biometrics.PromptContentItem {
+    ctor public PromptContentItemPlainText(@NonNull CharSequence);
     method public int describeContents();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentListItemPlainText> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentItemPlainText> CREATOR;
   }
 
   @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentView {
@@ -18778,7 +18778,7 @@
   @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
     method public int describeContents();
     method @Nullable public CharSequence getDescription();
-    method @NonNull public java.util.List<android.hardware.biometrics.PromptContentListItem> getListItems();
+    method @NonNull public java.util.List<android.hardware.biometrics.PromptContentItem> getListItems();
     method public static int getMaxEachItemCharacterNumber();
     method public static int getMaxItemCount();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -18787,7 +18787,8 @@
 
   public static final class PromptVerticalListContentView.Builder {
     ctor public PromptVerticalListContentView.Builder();
-    method @NonNull public android.hardware.biometrics.PromptVerticalListContentView.Builder addListItem(@NonNull android.hardware.biometrics.PromptContentListItem);
+    method @NonNull public android.hardware.biometrics.PromptVerticalListContentView.Builder addListItem(@NonNull android.hardware.biometrics.PromptContentItem);
+    method @NonNull public android.hardware.biometrics.PromptVerticalListContentView.Builder addListItem(@NonNull android.hardware.biometrics.PromptContentItem, int);
     method @NonNull public android.hardware.biometrics.PromptVerticalListContentView build();
     method @NonNull public android.hardware.biometrics.PromptVerticalListContentView.Builder setDescription(@NonNull CharSequence);
   }
@@ -23343,6 +23344,7 @@
     field public static final String KEY_HDR10_PLUS_INFO = "hdr10-plus-info";
     field public static final String KEY_HDR_STATIC_INFO = "hdr-static-info";
     field public static final String KEY_HEIGHT = "height";
+    field @FlaggedApi("com.android.media.codec.flags.codec_importance") public static final String KEY_IMPORTANCE = "importance";
     field public static final String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
     field public static final String KEY_IS_ADTS = "is-adts";
     field public static final String KEY_IS_AUTOSELECT = "is-autoselect";
@@ -26671,12 +26673,16 @@
 
   public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
     method @Nullable public static String getVideoResolution(String);
+    field @FlaggedApi("android.media.tv.flags.broadcast_visibility_types") public static final int BROADCAST_VISIBILITY_TYPE_INVISIBLE = 2; // 0x2
+    field @FlaggedApi("android.media.tv.flags.broadcast_visibility_types") public static final int BROADCAST_VISIBILITY_TYPE_NUMERIC_SELECTABLE_ONLY = 1; // 0x1
+    field @FlaggedApi("android.media.tv.flags.broadcast_visibility_types") public static final int BROADCAST_VISIBILITY_TYPE_VISIBLE = 0; // 0x0
     field public static final String COLUMN_APP_LINK_COLOR = "app_link_color";
     field public static final String COLUMN_APP_LINK_ICON_URI = "app_link_icon_uri";
     field public static final String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
     field public static final String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
     field public static final String COLUMN_APP_LINK_TEXT = "app_link_text";
     field public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+    field @FlaggedApi("android.media.tv.flags.broadcast_visibility_types") public static final String COLUMN_BROADCAST_VISIBILITY_TYPE = "broadcast_visibility_type";
     field public static final String COLUMN_BROWSABLE = "browsable";
     field public static final String COLUMN_CHANNEL_LIST_ID = "channel_list_id";
     field public static final String COLUMN_DESCRIPTION = "description";
@@ -31847,6 +31853,7 @@
     method public long computeChargeTimeRemaining();
     method public int getIntProperty(int);
     method public long getLongProperty(int);
+    method @FlaggedApi("android.os.battery_part_status_api") @Nullable public String getStringProperty(int);
     method public boolean isCharging();
     field public static final String ACTION_CHARGING = "android.os.action.CHARGING";
     field public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING";
@@ -43074,6 +43081,8 @@
     field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d26662d..e0c58d5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -133,6 +133,7 @@
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
     field public static final String GET_APP_METADATA = "android.permission.GET_APP_METADATA";
     field public static final String GET_APP_OPS_STATS = "android.permission.GET_APP_OPS_STATS";
+    field @FlaggedApi("android.app.bic_client") public static final String GET_BACKGROUND_INSTALLED_PACKAGES = "android.permission.GET_BACKGROUND_INSTALLED_PACKAGES";
     field @FlaggedApi("android.app.get_binding_uid_importance") public static final String GET_BINDING_UID_IMPORTANCE = "android.permission.GET_BINDING_UID_IMPORTANCE";
     field public static final String GET_HISTORICAL_APP_OPS_STATS = "android.permission.GET_HISTORICAL_APP_OPS_STATS";
     field public static final String GET_PROCESS_STATE_AND_OOM_SCORE = "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE";
@@ -622,6 +623,7 @@
     field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
     field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
+    field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS = "android:access_restricted_settings";
     field public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
     field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -3541,6 +3543,7 @@
     field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
+    field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String FONT_SERVICE = "font";
@@ -4219,11 +4222,14 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserProperties> CREATOR;
     field public static final int CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT = 1; // 0x1
     field public static final int CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION = 0; // 0x0
+    field public static final int CROSS_PROFILE_CONTENT_SHARING_UNKNOWN = -1; // 0xffffffff
     field public static final int SHOW_IN_QUIET_MODE_DEFAULT = 2; // 0x2
     field public static final int SHOW_IN_QUIET_MODE_HIDDEN = 1; // 0x1
     field public static final int SHOW_IN_QUIET_MODE_PAUSED = 0; // 0x0
+    field public static final int SHOW_IN_QUIET_MODE_UNKNOWN = -1; // 0xffffffff
     field public static final int SHOW_IN_SHARING_SURFACES_NO = 2; // 0x2
     field public static final int SHOW_IN_SHARING_SURFACES_SEPARATE = 1; // 0x1
+    field public static final int SHOW_IN_SHARING_SURFACES_UNKNOWN = -1; // 0xffffffff
     field public static final int SHOW_IN_SHARING_SURFACES_WITH_PARENT = 0; // 0x0
   }
 
@@ -10038,12 +10044,17 @@
     field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_CHARGING_POLICY = 9; // 0x9
     field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_FIRST_USAGE_DATE = 8; // 0x8
     field @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_MANUFACTURING_DATE = 7; // 0x7
+    field @FlaggedApi("android.os.battery_part_status_api") @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_PART_STATUS = 12; // 0xc
+    field @FlaggedApi("android.os.battery_part_status_api") @RequiresPermission(android.Manifest.permission.BATTERY_STATS) public static final int BATTERY_PROPERTY_SERIAL_NUMBER = 11; // 0xb
     field public static final int CHARGING_POLICY_ADAPTIVE_AC = 3; // 0x3
     field public static final int CHARGING_POLICY_ADAPTIVE_AON = 2; // 0x2
     field public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE = 4; // 0x4
     field public static final int CHARGING_POLICY_DEFAULT = 1; // 0x1
     field public static final String EXTRA_EVENTS = "android.os.extra.EVENTS";
     field public static final String EXTRA_EVENT_TIMESTAMP = "android.os.extra.EVENT_TIMESTAMP";
+    field @FlaggedApi("android.os.battery_part_status_api") public static final int PART_STATUS_ORIGINAL = 1; // 0x1
+    field @FlaggedApi("android.os.battery_part_status_api") public static final int PART_STATUS_REPLACED = 2; // 0x2
+    field @FlaggedApi("android.os.battery_part_status_api") public static final int PART_STATUS_UNSUPPORTED = 0; // 0x0
   }
 
   public final class BatterySaverPolicyConfig implements android.os.Parcelable {
@@ -14554,6 +14565,7 @@
     field public static final int EVENT_SERVICE_STATE_CHANGED = 1; // 0x1
     field public static final int EVENT_SIGNAL_STRENGTHS_CHANGED = 9; // 0x9
     field public static final int EVENT_SIGNAL_STRENGTH_CHANGED = 2; // 0x2
+    field @FlaggedApi("com.android.internal.telephony.flags.simultaneous_calling_indications") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED = 41; // 0x29
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_SRVCC_STATE_CHANGED = 16; // 0x10
     field public static final int EVENT_USER_MOBILE_DATA_STATE_CHANGED = 20; // 0x14
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_VOICE_ACTIVATION_STATE_CHANGED = 18; // 0x12
@@ -14600,6 +14612,10 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
   }
 
+  @FlaggedApi("com.android.internal.telephony.flags.simultaneous_calling_indications") public static interface TelephonyCallback.SimultaneousCellularCallingSupportListener {
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSimultaneousCellularCallingSubscriptionsChanged(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
   public static interface TelephonyCallback.SrvccStateListener {
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
   }
@@ -17154,6 +17170,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2; // 0x2
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
     field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bbe03a3..0d1d8d7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -284,16 +284,6 @@
     method public default void onOpActiveChanged(@NonNull String, int, @NonNull String, @Nullable String, boolean, int, int);
   }
 
-  public final class AutomaticZenRule implements android.os.Parcelable {
-    method @FlaggedApi("android.app.modes_api") public int getUserModifiedFields();
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_INTERRUPTION_FILTER = 2; // 0x2
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_NAME = 1; // 0x1
-  }
-
-  @FlaggedApi("android.app.modes_api") public static final class AutomaticZenRule.Builder {
-    method @FlaggedApi("android.app.modes_api") @NonNull public android.app.AutomaticZenRule.Builder setUserModifiedFields(int);
-  }
-
   public class BroadcastOptions extends android.app.ComponentOptions {
     ctor public BroadcastOptions();
     ctor public BroadcastOptions(@NonNull android.os.Bundle);
@@ -1177,6 +1167,7 @@
     method public int getShowInLauncher();
     field public static final int SHOW_IN_LAUNCHER_NO = 2; // 0x2
     field public static final int SHOW_IN_LAUNCHER_SEPARATE = 1; // 0x1
+    field public static final int SHOW_IN_LAUNCHER_UNKNOWN = -1; // 0xffffffff
     field public static final int SHOW_IN_LAUNCHER_WITH_PARENT = 0; // 0x0
   }
 
@@ -3021,47 +3012,8 @@
     method @Deprecated public boolean isBound();
   }
 
-  @FlaggedApi("android.app.modes_api") public final class ZenDeviceEffects implements android.os.Parcelable {
-    method public int getUserModifiedFields();
-    field public static final int FIELD_DIM_WALLPAPER = 4; // 0x4
-    field public static final int FIELD_DISABLE_AUTO_BRIGHTNESS = 16; // 0x10
-    field public static final int FIELD_DISABLE_TAP_TO_WAKE = 32; // 0x20
-    field public static final int FIELD_DISABLE_TILT_TO_WAKE = 64; // 0x40
-    field public static final int FIELD_DISABLE_TOUCH = 128; // 0x80
-    field public static final int FIELD_GRAYSCALE = 1; // 0x1
-    field public static final int FIELD_MAXIMIZE_DOZE = 512; // 0x200
-    field public static final int FIELD_MINIMIZE_RADIO_USAGE = 256; // 0x100
-    field public static final int FIELD_NIGHT_MODE = 8; // 0x8
-    field public static final int FIELD_SUPPRESS_AMBIENT_DISPLAY = 2; // 0x2
-  }
-
-  @FlaggedApi("android.app.modes_api") public static final class ZenDeviceEffects.Builder {
-    method @NonNull public android.service.notification.ZenDeviceEffects.Builder setUserModifiedFields(int);
-  }
-
-  public final class ZenPolicy implements android.os.Parcelable {
-    method @FlaggedApi("android.app.modes_api") public int getUserModifiedFields();
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_ALLOW_CHANNELS = 8; // 0x8
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_CALLS = 2; // 0x2
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_CONVERSATIONS = 4; // 0x4
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_MESSAGES = 1; // 0x1
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 128; // 0x80
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 32; // 0x20
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 256; // 0x100
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 64; // 0x40
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 512; // 0x200
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_AMBIENT = 32768; // 0x8000
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_BADGE = 16384; // 0x4000
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1024; // 0x400
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_LIGHTS = 2048; // 0x800
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 65536; // 0x10000
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_PEEK = 4096; // 0x1000
-    field @FlaggedApi("android.app.modes_api") public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 8192; // 0x2000
-  }
-
   public static final class ZenPolicy.Builder {
     ctor public ZenPolicy.Builder(@Nullable android.service.notification.ZenPolicy);
-    method @FlaggedApi("android.app.modes_api") @NonNull public android.service.notification.ZenPolicy.Builder setUserModifiedFields(int);
   }
 
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1db1caf..669baf9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2180,6 +2180,8 @@
      *
      * @hide
      */
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @SystemApi
     public static final String OPSTR_ACCESS_RESTRICTED_SETTINGS =
             "android:access_restricted_settings";
 
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 5b354fc..d57a4e5 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -23,7 +23,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.NotificationManager.InterruptionFilter;
 import android.content.ComponentName;
 import android.net.Uri;
@@ -113,8 +112,8 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
 
-    /** Used to track which rule variables have been modified by the user.
-     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+    /**
+     * Enum for the user-modifiable fields in this object.
      * @hide
      */
     @IntDef(flag = true, prefix = { "FIELD_" }, value = {
@@ -128,13 +127,11 @@
      * @hide
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
-    @TestApi
     public static final int FIELD_NAME = 1 << 0;
     /**
      * @hide
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
-    @TestApi
     public static final int FIELD_INTERRUPTION_FILTER = 1 << 1;
 
     private boolean enabled;
@@ -153,7 +150,6 @@
     private int mIconResId;
     private String mTriggerDescription;
     private boolean mAllowManualInvocation;
-    private @ModifiableField int mUserModifiedFields; // Bitwise representation
 
     /**
      * The maximum string length for any string contained in this automatic zen rule. This pertains
@@ -256,7 +252,6 @@
             mIconResId = source.readInt();
             mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
             mType = source.readInt();
-            mUserModifiedFields = source.readInt();
         }
     }
 
@@ -307,8 +302,7 @@
      * Returns whether this rule's name has been modified by the user.
      * @hide
      */
-    // TODO: b/310620812 - Replace with mUserModifiedFields & FIELD_NAME once
-    //  FLAG_MODES_API is inlined.
+    // TODO: b/310620812 - Consider removing completely. Seems not be used anywhere except tests.
     public boolean isModified() {
         return mModified;
     }
@@ -506,32 +500,6 @@
         return type;
     }
 
-    /**
-     * Gets the bitmask representing which fields are user modified. Bits are set using
-     * {@link ModifiableField}.
-     * @hide
-     */
-    @FlaggedApi(Flags.FLAG_MODES_API)
-    @TestApi
-    public @ModifiableField int getUserModifiedFields() {
-        return mUserModifiedFields;
-    }
-
-    /**
-     * Returns {@code true} if the {@link AutomaticZenRule} can be updated.
-     * When this returns {@code false}, calls to
-     * {@link NotificationManager#updateAutomaticZenRule(String, AutomaticZenRule)}) with this rule
-     * will ignore changes to user-configurable fields.
-     */
-    @FlaggedApi(Flags.FLAG_MODES_API)
-    public boolean canUpdate() {
-        // The rule is considered updateable if its bitmask has no user modifications, and
-        // the bitmasks of the policy and device effects have no modification.
-        return mUserModifiedFields == 0
-                && (mZenPolicy == null || mZenPolicy.getUserModifiedFields() == 0)
-                && (mDeviceEffects == null || mDeviceEffects.getUserModifiedFields() == 0);
-    }
-
     @Override
     public int describeContents() {
         return 0;
@@ -560,7 +528,6 @@
             dest.writeInt(mIconResId);
             dest.writeString(mTriggerDescription);
             dest.writeInt(mType);
-            dest.writeInt(mUserModifiedFields);
         }
     }
 
@@ -582,16 +549,14 @@
                     .append(",allowManualInvocation=").append(mAllowManualInvocation)
                     .append(",iconResId=").append(mIconResId)
                     .append(",triggerDescription=").append(mTriggerDescription)
-                    .append(",type=").append(mType)
-                    .append(",userModifiedFields=")
-                    .append(modifiedFieldsToString(mUserModifiedFields));
+                    .append(",type=").append(mType);
         }
 
         return sb.append(']').toString();
     }
 
-    @FlaggedApi(Flags.FLAG_MODES_API)
-    private String modifiedFieldsToString(int bitmask) {
+    /** @hide */
+    public static String fieldsToString(@ModifiableField int bitmask) {
         ArrayList<String> modified = new ArrayList<>();
         if ((bitmask & FIELD_NAME) != 0) {
             modified.add("FIELD_NAME");
@@ -623,8 +588,7 @@
                     && other.mAllowManualInvocation == mAllowManualInvocation
                     && other.mIconResId == mIconResId
                     && Objects.equals(other.mTriggerDescription, mTriggerDescription)
-                    && other.mType == mType
-                    && other.mUserModifiedFields == mUserModifiedFields;
+                    && other.mType == mType;
         }
         return finalEquals;
     }
@@ -634,8 +598,7 @@
         if (Flags.modesApi()) {
             return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
                     configurationActivity, mZenPolicy, mDeviceEffects, mModified, creationTime,
-                    mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType,
-                    mUserModifiedFields);
+                    mPkg, mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
         }
         return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
                 configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
@@ -704,7 +667,6 @@
         private boolean mAllowManualInvocation;
         private long mCreationTime;
         private String mPkg;
-        private @ModifiableField int mUserModifiedFields;
 
         public Builder(@NonNull AutomaticZenRule rule) {
             mName = rule.getName();
@@ -721,7 +683,6 @@
             mAllowManualInvocation = rule.isManualInvocationAllowed();
             mCreationTime = rule.getCreationTime();
             mPkg = rule.getPackageName();
-            mUserModifiedFields = rule.mUserModifiedFields;
         }
 
         public Builder(@NonNull String name, @NonNull Uri conditionId) {
@@ -848,19 +809,6 @@
             return this;
         }
 
-        /**
-         * Sets the bitmask representing which fields have been user-modified.
-         * This method should not be used outside of tests. The value of userModifiedFields
-         * should be set based on what values are changed when a rule is populated or updated..
-         * @hide
-         */
-        @FlaggedApi(Flags.FLAG_MODES_API)
-        @TestApi
-        public @NonNull Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
-            mUserModifiedFields = userModifiedFields;
-            return this;
-        }
-
         public @NonNull AutomaticZenRule build() {
             AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
                     mConditionId, mPolicy, mInterruptionFilter, mEnabled);
@@ -871,7 +819,6 @@
             rule.mIconResId = mIconResId;
             rule.mAllowManualInvocation = mAllowManualInvocation;
             rule.setPackageName(mPkg);
-            rule.mUserModifiedFields = mUserModifiedFields;
 
             return rule;
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 476232c..ed0cfbe 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5652,7 +5652,7 @@
                 pillColor = Colors.flattenAlpha(
                         getColors(p).getTertiaryFixedDimAccentColor(), bgColor);
                 textColor = Colors.flattenAlpha(
-                        getColors(p).getOnTertiaryAccentTextColor(), pillColor);
+                        getColors(p).getOnTertiaryFixedAccentTextColor(), pillColor);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9cf732a..d755413 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -31,6 +31,7 @@
 import android.app.blob.BlobStoreManagerFrameworkInitializer;
 import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IContentSuggestionsManager;
+import android.app.ecm.EnhancedConfirmationFrameworkInitializer;
 import android.app.job.JobSchedulerFrameworkInitializer;
 import android.app.people.PeopleManager;
 import android.app.prediction.AppPredictionManager;
@@ -1631,6 +1632,9 @@
             OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers();
             DeviceLockFrameworkInitializer.registerServiceWrappers();
             VirtualizationFrameworkInitializer.registerServiceWrappers();
+            if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
+                EnhancedConfirmationFrameworkInitializer.registerServiceWrappers();
+            }
         } finally {
             // If any of the above code throws, we're in a pretty bad shape and the process
             // will likely crash, but we'll reset it just in case there's an exception handler...
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4bc1237..249c0e43 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4242,6 +4242,7 @@
             VIRTUALIZATION_SERVICE,
             GRAMMATICAL_INFLECTION_SERVICE,
             SECURITY_STATE_SERVICE,
+           //@hide: ECM_ENHANCED_CONFIRMATION_SERVICE,
 
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -6527,6 +6528,18 @@
     public static final String SECURITY_STATE_SERVICE = "security_state";
 
     /**
+     * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.app.ecm.EnhancedConfirmationManager}.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.ecm.EnhancedConfirmationManager
+     * @hide
+     */
+    @FlaggedApi(android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED)
+    @SystemApi
+    public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 57749d4..269c6c2 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -123,6 +123,7 @@
      * @hide
      */
     @IntDef(prefix = "SHOW_IN_LAUNCHER_", value = {
+            SHOW_IN_LAUNCHER_UNKNOWN,
             SHOW_IN_LAUNCHER_WITH_PARENT,
             SHOW_IN_LAUNCHER_SEPARATE,
             SHOW_IN_LAUNCHER_NO,
@@ -131,6 +132,13 @@
     public @interface ShowInLauncher {
     }
     /**
+     * Indicates that the show in launcher value for this profile is unknown or unsupported.
+     * @hide
+     */
+    @TestApi
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_LAUNCHER_UNKNOWN = -1;
+    /**
      * Suggests that the launcher should show this user's apps in the main tab.
      * That is, either this user is a full user, so its apps should be presented accordingly, or, if
      * this user is a profile, then its apps should be shown alongside its parent's apps.
@@ -157,6 +165,7 @@
      * @hide
      */
     @IntDef(prefix = "SHOW_IN_SETTINGS_", value = {
+            SHOW_IN_SETTINGS_UNKNOWN,
             SHOW_IN_SETTINGS_WITH_PARENT,
             SHOW_IN_SETTINGS_SEPARATE,
             SHOW_IN_SETTINGS_NO,
@@ -165,6 +174,12 @@
     public @interface ShowInSettings {
     }
     /**
+     * Indicates that the show in settings value for this profile is unknown or unsupported.
+     * @hide
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_SETTINGS_UNKNOWN = -1;
+    /**
      * Suggests that the Settings app should show this user's apps in the main tab.
      * That is, either this user is a full user, so its apps should be presented accordingly, or, if
      * this user is a profile, then its apps should be shown alongside its parent's apps.
@@ -309,6 +324,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "SHOW_IN_QUIET_MODE_",
             value = {
+                    SHOW_IN_QUIET_MODE_UNKNOWN,
                     SHOW_IN_QUIET_MODE_PAUSED,
                     SHOW_IN_QUIET_MODE_HIDDEN,
                     SHOW_IN_QUIET_MODE_DEFAULT,
@@ -318,6 +334,12 @@
     }
 
     /**
+     * Indicates that the show in quiet mode value for this profile is unknown.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_QUIET_MODE_UNKNOWN = -1;
+
+    /**
      * Indicates that the profile should still be visible in quiet mode but should be shown as
      * paused (e.g. by greying out its icons).
      */
@@ -347,6 +369,7 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "SHOW_IN_SHARING_SURFACES_",
             value = {
+                    SHOW_IN_SHARING_SURFACES_UNKNOWN,
                     SHOW_IN_SHARING_SURFACES_SEPARATE,
                     SHOW_IN_SHARING_SURFACES_WITH_PARENT,
                     SHOW_IN_SHARING_SURFACES_NO,
@@ -356,6 +379,12 @@
     }
 
     /**
+     * Indicates that the show in launcher value for this profile is unknown or unsupported.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int SHOW_IN_SHARING_SURFACES_UNKNOWN = SHOW_IN_LAUNCHER_UNKNOWN;
+
+    /**
      * Indicates that the profile data and apps should be shown in sharing surfaces intermixed with
      * parent user's data and apps.
      */
@@ -379,7 +408,8 @@
      *
      * @hide
      */
-    @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_STRATEGY_"}, value = {
+    @IntDef(prefix = {"CROSS_PROFILE_CONTENT_SHARING_"}, value = {
+            CROSS_PROFILE_CONTENT_SHARING_UNKNOWN,
             CROSS_PROFILE_CONTENT_SHARING_NO_DELEGATION,
             CROSS_PROFILE_CONTENT_SHARING_DELEGATE_FROM_PARENT
     })
@@ -388,6 +418,13 @@
     }
 
     /**
+     * Signifies that cross-profile content sharing strategy, both to and from this profile, is
+     * unknown/unsupported.
+     */
+    @SuppressLint("UnflaggedApi") // b/306636213
+    public static final int CROSS_PROFILE_CONTENT_SHARING_UNKNOWN = -1;
+
+    /**
      * Signifies that cross-profile content sharing strategy, both to and from this profile, should
      * not be delegated to any other user/profile.
      * For ex:
diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java
index 088949e..f4312a9 100644
--- a/core/java/android/content/res/FontScaleConverter.java
+++ b/core/java/android/content/res/FontScaleConverter.java
@@ -17,7 +17,9 @@
 package android.content.res;
 
 
+import android.annotation.AnyThread;
 import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
 
 /**
  * A converter for non-linear font scaling. Converts font sizes given in "sp" dimensions to a
@@ -40,4 +42,35 @@
      * Converts a dimension in "dp" back to "sp".
      */
     float convertDpToSp(float dp);
+
+    /**
+     * Returns true if non-linear font scaling curves would be in effect for the given scale, false
+     * if the scaling would follow a linear curve or for no scaling.
+     *
+     * <p>Example usage: {@code
+     * isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)}
+     */
+    @AnyThread
+    static boolean isNonLinearFontScalingActive(float fontScale) {
+        return FontScaleConverterFactory.isNonLinearFontScalingActive(fontScale);
+    }
+
+    /**
+     * Finds a matching FontScaleConverter for the given fontScale factor.
+     *
+     * Generally you shouldn't need this; you can use {@link
+     * android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} directly and it will do
+     * the scaling conversion for you. Dimens and resources loaded from XML will also be
+     * automatically converted. But for UI frameworks or other situations where you need to do the
+     * conversion without an Android Context, you can use this method.
+     *
+     * @param fontScale the scale factor, usually from {@link Configuration#fontScale}.
+     *
+     * @return a converter for the given scale, or null if non-linear scaling should not be used.
+     */
+    @Nullable
+    @AnyThread
+    static FontScaleConverter forScale(float fontScale) {
+        return FontScaleConverterFactory.forScale(fontScale);
+    }
 }
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index 5d31cc0..cbe4c62 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -17,7 +17,6 @@
 package android.content.res;
 
 import android.annotation.AnyThread;
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.MathUtils;
@@ -32,8 +31,9 @@
  * android.util.TypedValue#applyDimension(int, float, DisplayMetrics)} directly and it will do the
  * scaling conversion for you. But for UI frameworks or other situations where you need to do the
  * conversion without an Android Context, you can use this class.
+ *
+ * @hide
  */
-@FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC)
 public class FontScaleConverterFactory {
     private static final float SCALE_KEY_MULTIPLIER = 100f;
 
@@ -124,7 +124,6 @@
      * <p>Example usage:
      * <code>isNonLinearFontScalingActive(getResources().getConfiguration().fontScale)</code>
      */
-    @FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC)
     @AnyThread
     public static boolean isNonLinearFontScalingActive(float fontScale) {
         return fontScale >= sMinScaleBeforeCurvesApplied;
@@ -137,7 +136,6 @@
      *
      * @return a converter for the given scale, or null if non-linear scaling should not be used.
      */
-    @FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC)
     @Nullable
     @AnyThread
     public static FontScaleConverter forScale(float fontScale) {
diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig
index f876eeb..1165f9a 100644
--- a/core/java/android/credentials/flags.aconfig
+++ b/core/java/android/credentials/flags.aconfig
@@ -33,4 +33,11 @@
     name: "new_settings_ui"
     description: "Enables new settings UI for VIC"
     bug: "315209085"
+}
+
+flag {
+    namespace: "credential_manager"
+    name: "selector_ui_improvements_enabled"
+    description: "Enables Credential Selector UI improvements for VIC"
+    bug: "319448437"
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index a0f4d8d..c0424db 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -16,6 +16,7 @@
 
 package android.hardware.biometrics;
 
+import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;
 import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -25,6 +26,7 @@
 import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.DrawableRes;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -33,6 +35,7 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.graphics.Bitmap;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
@@ -160,6 +163,45 @@
         }
 
         /**
+         * Optional: Sets the drawable resource of the logo that will be shown on the prompt.
+         *
+         * <p> Note that using this method is not recommended in most scenarios because the calling
+         * application's icon will be used by default. Setting the logo is intended for large
+         * bundled applications that perform a wide range of functions and need to show distinct
+         * icons for each function.
+         *
+         * @param logoRes A drawable resource of the logo that will be shown on the prompt.
+         * @return This builder.
+         */
+        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+        @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+        @NonNull
+        public BiometricPrompt.Builder setLogo(@DrawableRes int logoRes) {
+            mPromptInfo.setLogoRes(logoRes);
+            return this;
+        }
+
+        /**
+         * Optional: Sets the bitmap drawable of the logo that will be shown on the prompt.
+         *
+         * <p> Note that using this method is not recommended in most scenarios because the calling
+         * application's icon will be used by default. Setting the logo is intended for large
+         * bundled applications that perform a wide range of functions and need to show distinct
+         * icons for each function.
+         *
+         * @param logoBitmap A bitmap drawable of the logo that will be shown on the prompt.
+         * @return This builder.
+         */
+        @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+        @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+        @NonNull
+        public BiometricPrompt.Builder setLogo(@NonNull Bitmap logoBitmap) {
+            mPromptInfo.setLogoBitmap(logoBitmap);
+            return this;
+        }
+
+
+        /**
          * Required: Sets the title that will be shown on the prompt.
          * @param title The title to display.
          * @return This builder.
@@ -676,6 +718,34 @@
     }
 
     /**
+     * Gets the drawable resource of the logo for the prompt, as set by
+     * {@link Builder#setLogo(int)}. Currently for system applications use only.
+     *
+     * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
+     */
+    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+    @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+    @DrawableRes
+    public int getLogoRes() {
+        return mPromptInfo.getLogoRes();
+    }
+
+    /**
+     * Gets the logo bitmap for the prompt, as set by {@link Builder#setLogo(Bitmap)}. Currently for
+     * system applications use only.
+     *
+     * @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
+     */
+    @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+    @RequiresPermission(MANAGE_BIOMETRIC_DIALOG)
+    @Nullable
+    public Bitmap getLogoBitmap() {
+        return mPromptInfo.getLogoBitmap();
+    }
+
+
+
+    /**
      * Gets the title for the prompt, as set by {@link Builder#setTitle(CharSequence)}.
      * @return The title of the prompt, which is guaranteed to be non-null.
      */
diff --git a/core/java/android/hardware/biometrics/PromptContentListItem.java b/core/java/android/hardware/biometrics/PromptContentItem.java
similarity index 88%
rename from core/java/android/hardware/biometrics/PromptContentListItem.java
rename to core/java/android/hardware/biometrics/PromptContentItem.java
index fa3783d..c47b37a 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItem.java
+++ b/core/java/android/hardware/biometrics/PromptContentItem.java
@@ -21,9 +21,9 @@
 import android.annotation.FlaggedApi;
 
 /**
- * A list item shown on {@link PromptVerticalListContentView}.
+ * An item shown on {@link PromptContentView}.
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public interface PromptContentListItem {
+public interface PromptContentItem {
 }
 
diff --git a/core/java/android/hardware/biometrics/PromptContentListItemBulletedText.java b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
similarity index 75%
rename from core/java/android/hardware/biometrics/PromptContentListItemBulletedText.java
rename to core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
index c31f8a6..c5e5a80 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItemBulletedText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemBulletedText.java
@@ -27,7 +27,7 @@
  * A list item with bulleted text shown on {@link PromptVerticalListContentView}.
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public final class PromptContentListItemBulletedText implements PromptContentListItemParcelable {
+public final class PromptContentItemBulletedText implements PromptContentItemParcelable {
     private final CharSequence mText;
 
     /**
@@ -35,7 +35,7 @@
      *
      * @param text The text of this list item.
      */
-    public PromptContentListItemBulletedText(@NonNull CharSequence text) {
+    public PromptContentItemBulletedText(@NonNull CharSequence text) {
         mText = text;
     }
 
@@ -67,15 +67,15 @@
      * @see Parcelable.Creator
      */
     @NonNull
-    public static final Creator<PromptContentListItemBulletedText> CREATOR = new Creator<>() {
+    public static final Creator<PromptContentItemBulletedText> CREATOR = new Creator<>() {
         @Override
-        public PromptContentListItemBulletedText createFromParcel(Parcel in) {
-            return new PromptContentListItemBulletedText(in.readCharSequence());
+        public PromptContentItemBulletedText createFromParcel(Parcel in) {
+            return new PromptContentItemBulletedText(in.readCharSequence());
         }
 
         @Override
-        public PromptContentListItemBulletedText[] newArray(int size) {
-            return new PromptContentListItemBulletedText[size];
+        public PromptContentItemBulletedText[] newArray(int size) {
+            return new PromptContentItemBulletedText[size];
         }
     };
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentListItemParcelable.java b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
similarity index 79%
rename from core/java/android/hardware/biometrics/PromptContentListItemParcelable.java
rename to core/java/android/hardware/biometrics/PromptContentItemParcelable.java
index 15271f0..668912cf 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItemParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemParcelable.java
@@ -22,9 +22,9 @@
 import android.os.Parcelable;
 
 /**
- * A parcelable {@link PromptContentListItem}.
+ * A parcelable {@link PromptContentItem}.
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-sealed interface PromptContentListItemParcelable extends PromptContentListItem, Parcelable
-        permits PromptContentListItemPlainText, PromptContentListItemBulletedText {
+sealed interface PromptContentItemParcelable extends PromptContentItem, Parcelable
+        permits PromptContentItemPlainText, PromptContentItemBulletedText {
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentListItemPlainText.java b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
similarity index 76%
rename from core/java/android/hardware/biometrics/PromptContentListItemPlainText.java
rename to core/java/android/hardware/biometrics/PromptContentItemPlainText.java
index d72a758..6434c59 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItemPlainText.java
+++ b/core/java/android/hardware/biometrics/PromptContentItemPlainText.java
@@ -27,7 +27,7 @@
  * A list item with plain text shown on {@link PromptVerticalListContentView}.
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public final class PromptContentListItemPlainText implements PromptContentListItemParcelable {
+public final class PromptContentItemPlainText implements PromptContentItemParcelable {
     private final CharSequence mText;
 
     /**
@@ -35,7 +35,7 @@
      *
      * @param text The text of this list item.
      */
-    public PromptContentListItemPlainText(@NonNull CharSequence text) {
+    public PromptContentItemPlainText(@NonNull CharSequence text) {
         mText = text;
     }
 
@@ -67,15 +67,15 @@
      * @see Parcelable.Creator
      */
     @NonNull
-    public static final Creator<PromptContentListItemPlainText> CREATOR = new Creator<>() {
+    public static final Creator<PromptContentItemPlainText> CREATOR = new Creator<>() {
         @Override
-        public PromptContentListItemPlainText createFromParcel(Parcel in) {
-            return new PromptContentListItemPlainText(in.readCharSequence());
+        public PromptContentItemPlainText createFromParcel(Parcel in) {
+            return new PromptContentItemPlainText(in.readCharSequence());
         }
 
         @Override
-        public PromptContentListItemPlainText[] newArray(int size) {
-            return new PromptContentListItemPlainText[size];
+        public PromptContentItemPlainText[] newArray(int size) {
+            return new PromptContentItemPlainText[size];
         }
     };
 }
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index c73ebd4..d788b37 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -16,8 +16,10 @@
 
 package android.hardware.biometrics;
 
+import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Bitmap;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -30,6 +32,8 @@
  */
 public class PromptInfo implements Parcelable {
 
+    @DrawableRes private int mLogoRes = -1;
+    @Nullable private Bitmap mLogoBitmap;
     @NonNull private CharSequence mTitle;
     private boolean mUseDefaultTitle;
     @Nullable private CharSequence mSubtitle;
@@ -56,6 +60,8 @@
     }
 
     PromptInfo(Parcel in) {
+        mLogoRes = in.readInt();
+        mLogoBitmap = in.readTypedObject(Bitmap.CREATOR);
         mTitle = in.readCharSequence();
         mUseDefaultTitle = in.readBoolean();
         mSubtitle = in.readCharSequence();
@@ -98,6 +104,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mLogoRes);
+        dest.writeTypedObject(mLogoBitmap, 0);
         dest.writeCharSequence(mTitle);
         dest.writeBoolean(mUseDefaultTitle);
         dest.writeCharSequence(mSubtitle);
@@ -156,9 +164,30 @@
         }
         return false;
     }
+
+    /**
+     * Returns whether MANAGE_BIOMETRIC_DIALOG is contained.
+     */
+    public boolean containsManageBioApiConfigurations() {
+        if (mLogoRes != -1) {
+            return true;
+        } else if (mLogoBitmap != null) {
+            return true;
+        }
+        return false;
+    }
     // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
 
     // Setters
+    public void setLogoRes(@DrawableRes int logoRes) {
+        mLogoRes = logoRes;
+        checkOnlyOneLogoSet();
+    }
+
+    public void setLogoBitmap(@NonNull Bitmap logoBitmap) {
+        mLogoBitmap = logoBitmap;
+        checkOnlyOneLogoSet();
+    }
 
     public void setTitle(CharSequence title) {
         mTitle = title;
@@ -244,6 +273,14 @@
     }
 
     // Getters
+    @DrawableRes
+    public int getLogoRes() {
+        return mLogoRes;
+    }
+
+    public Bitmap getLogoBitmap() {
+        return mLogoBitmap;
+    }
 
     public CharSequence getTitle() {
         return mTitle;
@@ -337,4 +374,11 @@
     public boolean isShowEmergencyCallButton() {
         return mShowEmergencyCallButton;
     }
+
+    private void checkOnlyOneLogoSet() {
+        if (mLogoRes != -1 && mLogoBitmap != null) {
+            throw new IllegalStateException(
+                    "Exclusively one of logo resource or logo bitmap can be set");
+        }
+    }
 }
diff --git a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
index f3cb189..f3e6290 100644
--- a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
+++ b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
@@ -40,9 +40,9 @@
  *     .setSubTitle(...)
  *     .setContentView(new PromptVerticalListContentView.Builder()
  *         .setDescription("test description")
- *         .addListItem(new PromptContentListItemPlainText("test item 1"))
- *         .addListItem(new PromptContentListItemPlainText("test item 2"))
- *         .addListItem(new PromptContentListItemBulletedText("test item 3"))
+ *         .addListItem(new PromptContentItemPlainText("test item 1"))
+ *         .addListItem(new PromptContentItemPlainText("test item 2"))
+ *         .addListItem(new PromptContentItemBulletedText("test item 3"))
  *         .build())
  *     .build();
  * </pre>
@@ -51,11 +51,11 @@
 public final class PromptVerticalListContentView implements PromptContentViewParcelable {
     private static final int MAX_ITEM_NUMBER = 20;
     private static final int MAX_EACH_ITEM_CHARACTER_NUMBER = 640;
-    private final List<PromptContentListItemParcelable> mContentList;
+    private final List<PromptContentItemParcelable> mContentList;
     private final CharSequence mDescription;
 
     private PromptVerticalListContentView(
-            @NonNull List<PromptContentListItemParcelable> contentList,
+            @NonNull List<PromptContentItemParcelable> contentList,
             @NonNull CharSequence description) {
         mContentList = contentList;
         mDescription = description;
@@ -63,8 +63,8 @@
 
     private PromptVerticalListContentView(Parcel in) {
         mContentList = in.readArrayList(
-                PromptContentListItemParcelable.class.getClassLoader(),
-                PromptContentListItemParcelable.class);
+                PromptContentItemParcelable.class.getClassLoader(),
+                PromptContentItemParcelable.class);
         mDescription = in.readCharSequence();
     }
 
@@ -94,13 +94,13 @@
     }
 
     /**
-     * Gets the list of ListItem on the content view, as set by
-     * {@link PromptVerticalListContentView.Builder#addListItem(PromptContentListItem)}.
+     * Gets the list of items on the content view, as set by
+     * {@link PromptVerticalListContentView.Builder#addListItem(PromptContentItem)}.
      *
      * @return The item list on the content view.
      */
     @NonNull
-    public List<PromptContentListItem> getListItems() {
+    public List<PromptContentItem> getListItems() {
         return new ArrayList<>(mContentList);
     }
 
@@ -142,7 +142,7 @@
      * A builder that collects arguments to be shown on the vertical list view.
      */
     public static final class Builder {
-        private final List<PromptContentListItemParcelable> mContentList = new ArrayList<>();
+        private final List<PromptContentItemParcelable> mContentList = new ArrayList<>();
         private CharSequence mDescription;
 
         /**
@@ -159,28 +159,50 @@
 
         /**
          * Optional: Adds a list item in the current row. Maximum {@value MAX_ITEM_NUMBER} items in
-         * total.
+         * total. The maximum length for each item is {@value MAX_EACH_ITEM_CHARACTER_NUMBER}
+         * characters.
          *
          * @param listItem The list item view to display
          * @return This builder.
          */
         @NonNull
-        public Builder addListItem(@NonNull PromptContentListItem listItem) {
+        public Builder addListItem(@NonNull PromptContentItem listItem) {
             if (doesListItemExceedsCharLimit(listItem)) {
                 throw new IllegalStateException(
                         "The character number of list item exceeds "
                                 + MAX_EACH_ITEM_CHARACTER_NUMBER);
             }
-            mContentList.add((PromptContentListItemParcelable) listItem);
+            mContentList.add((PromptContentItemParcelable) listItem);
             return this;
         }
 
-        private boolean doesListItemExceedsCharLimit(PromptContentListItem listItem) {
-            if (listItem instanceof PromptContentListItemPlainText) {
-                return ((PromptContentListItemPlainText) listItem).getText().length()
+
+        /**
+         * Optional: Adds a list item in the current row. Maximum {@value MAX_ITEM_NUMBER} items in
+         * total. The maximum length for each item is {@value MAX_EACH_ITEM_CHARACTER_NUMBER}
+         * characters.
+         *
+         * @param listItem The list item view to display
+         * @param index The position at which to add the item
+         * @return This builder.
+         */
+        @NonNull
+        public Builder addListItem(@NonNull PromptContentItem listItem, int index) {
+            if (doesListItemExceedsCharLimit(listItem)) {
+                throw new IllegalStateException(
+                        "The character number of list item exceeds "
+                                + MAX_EACH_ITEM_CHARACTER_NUMBER);
+            }
+            mContentList.add(index, (PromptContentItemParcelable) listItem);
+            return this;
+        }
+
+        private boolean doesListItemExceedsCharLimit(PromptContentItem listItem) {
+            if (listItem instanceof PromptContentItemPlainText) {
+                return ((PromptContentItemPlainText) listItem).getText().length()
                         > MAX_EACH_ITEM_CHARACTER_NUMBER;
-            } else if (listItem instanceof PromptContentListItemBulletedText) {
-                return ((PromptContentListItemBulletedText) listItem).getText().length()
+            } else if (listItem instanceof PromptContentItemBulletedText) {
+                return ((PromptContentItemBulletedText) listItem).getText().length()
                         > MAX_EACH_ITEM_CHARACTER_NUMBER;
             } else {
                 return false;
diff --git a/core/java/android/hardware/radio/TEST_MAPPING b/core/java/android/hardware/radio/TEST_MAPPING
new file mode 100644
index 0000000..ee4eeb6
--- /dev/null
+++ b/core/java/android/hardware/radio/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/core/tests/BroadcastRadioTests"
+    }
+  ]
+}
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 25fba60..b9bb059 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -17,9 +17,11 @@
 package android.os;
 
 import static android.os.Flags.FLAG_STATE_OF_HEALTH_PUBLIC;
+import static android.os.Flags.FLAG_BATTERY_PART_STATUS_API;
 
 import android.Manifest.permission;
 import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -236,6 +238,31 @@
     public static final int CHARGING_POLICY_ADAPTIVE_LONGLIFE =
                                             OsProtoEnums.CHARGING_POLICY_ADAPTIVE_LONGLIFE; // = 4
 
+    // values for "battery part status" property
+    /**
+     * Battery part status is not supported.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public static final int PART_STATUS_UNSUPPORTED = 0;
+
+    /**
+     * Battery is the original device battery.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public static final int PART_STATUS_ORIGINAL = 1;
+
+    /**
+     * Battery has been replaced.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public static final int PART_STATUS_REPLACED = 2;
+
     /** @hide */
     @SuppressLint("UnflaggedApi") // TestApi without associated feature.
     @TestApi
@@ -366,6 +393,32 @@
     @FlaggedApi(FLAG_STATE_OF_HEALTH_PUBLIC)
     public static final int BATTERY_PROPERTY_STATE_OF_HEALTH = 10;
 
+    /**
+     * Battery part serial number.
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public static final int BATTERY_PROPERTY_SERIAL_NUMBER = 11;
+
+    /**
+     * Battery part status from a BATTERY_PART_STATUS_* value.
+     *
+     * <p class="note">
+     * The sender must hold the {@link android.Manifest.permission#BATTERY_STATS} permission.
+     *
+     * @hide
+     */
+    @RequiresPermission(permission.BATTERY_STATS)
+    @SystemApi
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public static final int BATTERY_PROPERTY_PART_STATUS = 12;
+
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
     private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
@@ -431,6 +484,25 @@
     }
 
     /**
+     * Same as queryProperty, but for strings.
+     */
+    private String queryStringProperty(int id) {
+        if (mBatteryPropertiesRegistrar == null) {
+            return null;
+        }
+
+        try {
+            BatteryProperty prop = new BatteryProperty();
+            if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) {
+                return prop.getString();
+            }
+            return null;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the value of a battery property of integer type.
      *
      * @param id identifier of the requested property
@@ -464,6 +536,21 @@
     }
 
     /**
+     * Return the value of a battery property of String type. If the
+     * platform does not provide the property queried, this value will
+     * be null.
+     *
+     * @param id identifier of the requested property.
+     *
+     * @return the property value, or null if not supported.
+     */
+    @Nullable
+    @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+    public String getStringProperty(int id) {
+        return queryStringProperty(id);
+    }
+
+    /**
      * Return true if the plugType given is wired
      * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
      *        or {@link #BATTERY_PLUGGED_WIRELESS}
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index b40988a..464577f 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -28,12 +28,14 @@
  */
 public class BatteryProperty implements Parcelable {
     private long mValueLong;
+    private String mValueString;
 
     /**
      * @hide
      */
     public BatteryProperty() {
         mValueLong = Long.MIN_VALUE;
+        mValueString = null;
     }
 
     /**
@@ -46,14 +48,23 @@
     /**
      * @hide
      */
+    public String getString() {
+        return mValueString;
+    }
+
+    /**
+     * @hide
+     */
     public void setLong(long val) {
         mValueLong = val;
     }
 
-    /*
-     * Parcel read/write code must be kept in sync with
-     * frameworks/native/services/batteryservice/BatteryProperty.cpp
+    /**
+     * @hide
      */
+    public void setString(String val) {
+        mValueString = val;
+    }
 
     private BatteryProperty(Parcel p) {
         readFromParcel(p);
@@ -61,10 +72,12 @@
 
     public void readFromParcel(Parcel p) {
         mValueLong = p.readLong();
+        mValueString = p.readString8();
     }
 
     public void writeToParcel(Parcel p, int flags) {
         p.writeLong(mValueLong);
+        p.writeString8(mValueString);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<BatteryProperty> CREATOR
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 0db90bf..82518bf 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -106,3 +106,11 @@
     bug: "305311707"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "battery_part_status_api"
+    namespace: "phoenix"
+    description: "Feature flag for adding Health HAL v3 APIs."
+    is_fixed_read_only: true
+    bug: "309792384"
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index bced217..db8f52c 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -45,7 +45,8 @@
 }
 
 flag {
-    name: "enhanced_confirmation_mode_apis"
+    name: "enhanced_confirmation_mode_apis_enabled"
+    is_fixed_read_only: true
     namespace: "permissions"
     description: "enable enhanced confirmation mode apis"
     bug: "310220212"
@@ -85,3 +86,10 @@
     description: "This flag is used to enabled the Wallet Role for all users on the device"
     bug: "283989236"
 }
+
+flag {
+  name: "runtime_permission_appops_mapping"
+  namespace: "permissions"
+  description: "Use runtime permission state to determine appop state"
+  bug: "266164193"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7d84bb3..7d94dd2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3176,15 +3176,7 @@
         }
 
         public void destroy() {
-            try {
-                // If this process is the system server process, mArray is the same object as
-                // the memory int array kept inside SettingsProvider, so skipping the close()
-                if (!Settings.isInSystemServer() && !mArray.isClosed()) {
-                    mArray.close();
-                }
-            } catch (IOException e) {
-                Log.e(TAG, "Error closing backing array", e);
-            }
+            maybeCloseGenerationArray(mArray);
         }
 
         @Override
@@ -3197,6 +3189,21 @@
         }
     }
 
+    private static void maybeCloseGenerationArray(@Nullable MemoryIntArray array) {
+        if (array == null) {
+            return;
+        }
+        try {
+            // If this process is the system server process, the MemoryIntArray received from Parcel
+            // is the same object as the one kept inside SettingsProvider, so skipping the close().
+            if (!Settings.isInSystemServer() && !array.isClosed()) {
+                array.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Error closing the generation tracking array", e);
+        }
+    }
+
     private static final class ContentProviderHolder {
         private final Object mLock = new Object();
 
@@ -3236,9 +3243,17 @@
 
         private static final String NAME_EQ_PLACEHOLDER = "name=?";
 
+        // Cached values of queried settings.
+        // Key is the setting's name, value is the setting's value.
         // Must synchronize on 'this' to access mValues and mValuesVersion.
         private final ArrayMap<String, String> mValues = new ArrayMap<>();
 
+        // Cached values for queried prefixes.
+        // Key is the prefix, value is all of the settings under the prefix, mapped from a setting's
+        // name to a setting's value. The name string doesn't include the prefix.
+        // Must synchronize on 'this' to access.
+        private final ArrayMap<String, ArrayMap<String, String>> mPrefixToValues = new ArrayMap<>();
+
         private final Uri mUri;
         @UnsupportedAppUsage
         private final ContentProviderHolder mProviderHolder;
@@ -3496,6 +3511,8 @@
                                         mGenerationTrackers.put(name, new GenerationTracker(name,
                                                 array, index, generation,
                                                 mGenerationTrackerErrorHandler));
+                                    } else {
+                                        maybeCloseGenerationArray(array);
                                     }
                                 }
                                 if (mGenerationTrackers.get(name) != null
@@ -3583,15 +3600,13 @@
                     || applicationInfo.isSignedWithPlatformKey();
         }
 
-        private ArrayMap<String, String> getStringsForPrefixStripPrefix(
-                ContentResolver cr, String prefix, String[] names) {
+        private Map<String, String> getStringsForPrefixStripPrefix(
+                ContentResolver cr, String prefix, List<String> names) {
             String namespace = prefix.substring(0, prefix.length() - 1);
             ArrayMap<String, String> keyValues = new ArrayMap<>();
             int substringLength = prefix.length();
-
             int currentGeneration = -1;
             boolean needsGenerationTracker = false;
-
             synchronized (NameValueCache.this) {
                 final GenerationTracker generationTracker = mGenerationTrackers.get(prefix);
                 if (generationTracker != null) {
@@ -3605,40 +3620,22 @@
                         // generation tracker and request a new one
                         generationTracker.destroy();
                         mGenerationTrackers.remove(prefix);
-                        for (int i = mValues.size() - 1; i >= 0; i--) {
-                            String key = mValues.keyAt(i);
-                            if (key.startsWith(prefix)) {
-                                mValues.remove(key);
-                            }
-                        }
+                        mPrefixToValues.remove(prefix);
                         needsGenerationTracker = true;
                     } else {
-                        boolean prefixCached = mValues.containsKey(prefix);
-                        if (prefixCached) {
-                            if (DEBUG) {
-                                Log.i(TAG, "Cache hit for prefix:" + prefix);
-                            }
-                            if (names.length > 0) {
+                        final ArrayMap<String, String> cachedSettings = mPrefixToValues.get(prefix);
+                        if (cachedSettings != null) {
+                            if (!names.isEmpty()) {
                                 for (String name : names) {
-                                    // mValues can contain "null" values, need to use containsKey.
-                                    if (mValues.containsKey(name)) {
+                                    // The cache can contain "null" values, need to use containsKey.
+                                    if (cachedSettings.containsKey(name)) {
                                         keyValues.put(
-                                                name.substring(substringLength),
-                                                mValues.get(name));
+                                                name,
+                                                cachedSettings.get(name));
                                     }
                                 }
                             } else {
-                                for (int i = 0; i < mValues.size(); ++i) {
-                                    String key = mValues.keyAt(i);
-                                    // Explicitly exclude the prefix as it is only there to
-                                    // signal that the prefix has been cached.
-                                    if (key.startsWith(prefix) && !key.equals(prefix)) {
-                                        String value = mValues.valueAt(i);
-                                        keyValues.put(
-                                                key.substring(substringLength),
-                                                value);
-                                    }
-                                }
+                                keyValues.putAll(cachedSettings);
                             }
                             return keyValues;
                         }
@@ -3648,7 +3645,6 @@
                     needsGenerationTracker = true;
                 }
             }
-
             if (mCallListCommand == null) {
                 // No list command specified, return empty map
                 return keyValues;
@@ -3693,20 +3689,23 @@
                 }
 
                 // All flags for the namespace
-                Map<String, String> flagsToValues =
+                HashMap<String, String> flagsToValues =
                         (HashMap) b.getSerializable(Settings.NameValueTable.VALUE, java.util.HashMap.class);
+                if (flagsToValues == null) {
+                    return keyValues;
+                }
                 // Only the flags requested by the caller
-                if (names.length > 0) {
+                if (!names.isEmpty()) {
                     for (String name : names) {
                         // flagsToValues can contain "null" values, need to use containsKey.
-                        if (flagsToValues.containsKey(name)) {
+                        final String key = Config.createCompositeName(namespace, name);
+                        if (flagsToValues.containsKey(key)) {
                             keyValues.put(
-                                    name.substring(substringLength),
-                                    flagsToValues.get(name));
+                                    name,
+                                    flagsToValues.get(key));
                         }
                     }
                 } else {
-                    keyValues.ensureCapacity(keyValues.size() + flagsToValues.size());
                     for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
                         keyValues.put(
                                 flag.getKey().substring(substringLength),
@@ -3733,6 +3732,8 @@
                                     new GenerationTracker(prefix, array, index, generation,
                                             mGenerationTrackerErrorHandler));
                             currentGeneration = generation;
+                        } else {
+                            maybeCloseGenerationArray(array);
                         }
                     }
                     if (mGenerationTrackers.get(prefix) != null && currentGeneration
@@ -3740,10 +3741,18 @@
                         if (DEBUG) {
                             Log.i(TAG, "Updating cache for prefix:" + prefix);
                         }
-                        // cache the complete list of flags for the namespace
-                        mValues.putAll(flagsToValues);
-                        // Adding the prefix as a signal that the prefix is cached.
-                        mValues.put(prefix, null);
+                        // Cache the complete list of flags for the namespace for bulk queries.
+                        // In this cached list, the setting's name doesn't include the prefix.
+                        ArrayMap<String, String> namesToValues =
+                                new ArrayMap<>(flagsToValues.size() + 1);
+                        for (Map.Entry<String, String> flag : flagsToValues.entrySet()) {
+                            namesToValues.put(
+                                    flag.getKey().substring(substringLength),
+                                    flag.getValue());
+                        }
+                        // Legacy behavior, we return <"", null> when queried with name = ""
+                        namesToValues.put("", null);
+                        mPrefixToValues.put(prefix, namesToValues);
                     }
                 }
                 return keyValues;
@@ -19907,16 +19916,9 @@
         @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
         public static Map<String, String> getStrings(@NonNull ContentResolver resolver,
                 @NonNull String namespace, @NonNull List<String> names) {
-            String[] compositeNames = new String[names.size()];
-            for (int i = 0, size = names.size(); i < size; ++i) {
-                compositeNames[i] = createCompositeName(namespace, names.get(i));
-            }
-
             String prefix = createPrefix(namespace);
 
-            ArrayMap<String, String> keyValues = sNameValueCache.getStringsForPrefixStripPrefix(
-                    resolver, prefix, compositeNames);
-            return keyValues;
+            return sNameValueCache.getStringsForPrefixStripPrefix(resolver, prefix, names);
         }
 
         /**
@@ -20238,7 +20240,7 @@
             }
         }
 
-        private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
+        static String createCompositeName(@NonNull String namespace, @NonNull String name) {
             Preconditions.checkNotNull(namespace);
             Preconditions.checkNotNull(name);
             var sb = new StringBuilder(namespace.length() + 1 + name.length());
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
index 8b4a99e..d5ac7a7 100644
--- a/core/java/android/provider/TEST_MAPPING
+++ b/core/java/android/provider/TEST_MAPPING
@@ -28,5 +28,10 @@
                 }
             ]
         }
+    ],
+    "postsubmit": [
+        {
+            "name": "CtsDeviceConfigTestCases"
+        }
     ]
 }
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 30524a1..1994058 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -53,7 +53,7 @@
 
 flag {
     name: "frp_enforcement"
-    namespace: "android_hw_security"
+    namespace: "hardware_backed_security"
     description: "This flag controls whether PDB enforces FRP"
     bug: "290312729"
     is_fixed_read_only: true
diff --git a/core/java/android/service/notification/ZenDeviceEffects.java b/core/java/android/service/notification/ZenDeviceEffects.java
index 03ebae5..90049e6 100644
--- a/core/java/android/service/notification/ZenDeviceEffects.java
+++ b/core/java/android/service/notification/ZenDeviceEffects.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -37,8 +36,8 @@
 @FlaggedApi(Flags.FLAG_MODES_API)
 public final class ZenDeviceEffects implements Parcelable {
 
-    /** Used to track which rule variables have been modified by the user.
-     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+    /**
+     * Enum for the user-modifiable fields in this object.
      * @hide
      */
     @IntDef(flag = true, prefix = { "FIELD_" }, value = {
@@ -59,52 +58,42 @@
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_GRAYSCALE = 1 << 0;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_SUPPRESS_AMBIENT_DISPLAY = 1 << 1;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_DIM_WALLPAPER = 1 << 2;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_NIGHT_MODE = 1 << 3;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_DISABLE_AUTO_BRIGHTNESS = 1 << 4;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_DISABLE_TAP_TO_WAKE = 1 << 5;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_DISABLE_TILT_TO_WAKE = 1 << 6;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_DISABLE_TOUCH = 1 << 7;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_MINIMIZE_RADIO_USAGE = 1 << 8;
     /**
      * @hide
      */
-    @TestApi
     public static final int FIELD_MAXIMIZE_DOZE = 1 << 9;
 
     private final boolean mGrayscale;
@@ -119,13 +108,10 @@
     private final boolean mMinimizeRadioUsage;
     private final boolean mMaximizeDoze;
 
-    private final @ModifiableField int mUserModifiedFields; // Bitwise representation
-
     private ZenDeviceEffects(boolean grayscale, boolean suppressAmbientDisplay,
             boolean dimWallpaper, boolean nightMode, boolean disableAutoBrightness,
             boolean disableTapToWake, boolean disableTiltToWake, boolean disableTouch,
-            boolean minimizeRadioUsage, boolean maximizeDoze,
-            @ModifiableField int userModifiedFields) {
+            boolean minimizeRadioUsage, boolean maximizeDoze) {
         mGrayscale = grayscale;
         mSuppressAmbientDisplay = suppressAmbientDisplay;
         mDimWallpaper = dimWallpaper;
@@ -136,7 +122,6 @@
         mDisableTouch = disableTouch;
         mMinimizeRadioUsage = minimizeRadioUsage;
         mMaximizeDoze = maximizeDoze;
-        mUserModifiedFields = userModifiedFields;
     }
 
     @Override
@@ -153,15 +138,14 @@
                 && this.mDisableTiltToWake == that.mDisableTiltToWake
                 && this.mDisableTouch == that.mDisableTouch
                 && this.mMinimizeRadioUsage == that.mMinimizeRadioUsage
-                && this.mMaximizeDoze == that.mMaximizeDoze
-                && this.mUserModifiedFields == that.mUserModifiedFields;
+                && this.mMaximizeDoze == that.mMaximizeDoze;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mGrayscale, mSuppressAmbientDisplay, mDimWallpaper, mNightMode,
                 mDisableAutoBrightness, mDisableTapToWake, mDisableTiltToWake, mDisableTouch,
-                mMinimizeRadioUsage, mMaximizeDoze, mUserModifiedFields);
+                mMinimizeRadioUsage, mMaximizeDoze);
     }
 
     @Override
@@ -177,11 +161,11 @@
         if (mDisableTouch) effects.add("disableTouch");
         if (mMinimizeRadioUsage) effects.add("minimizeRadioUsage");
         if (mMaximizeDoze) effects.add("maximizeDoze");
-        return "[" + String.join(", ", effects) + "]"
-                + " userModifiedFields: " + modifiedFieldsToString(mUserModifiedFields);
+        return "[" + String.join(", ", effects) + "]";
     }
 
-    private String modifiedFieldsToString(int bitmask) {
+    /** @hide */
+    public static String fieldsToString(@ModifiableField int bitmask) {
         ArrayList<String> modified = new ArrayList<>();
         if ((bitmask & FIELD_GRAYSCALE) != 0) {
             modified.add("FIELD_GRAYSCALE");
@@ -312,7 +296,7 @@
             return new ZenDeviceEffects(in.readBoolean(),
                     in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
                     in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readBoolean(),
-                    in.readBoolean(), in.readInt());
+                    in.readBoolean());
         }
 
         @Override
@@ -321,16 +305,6 @@
         }
     };
 
-    /**
-     * Gets the bitmask representing which fields are user modified. Bits are set using
-     * {@link ModifiableField}.
-     * @hide
-     */
-    @TestApi
-    public @ModifiableField int getUserModifiedFields() {
-        return mUserModifiedFields;
-    }
-
     @Override
     public int describeContents() {
         return 0;
@@ -348,7 +322,6 @@
         dest.writeBoolean(mDisableTouch);
         dest.writeBoolean(mMinimizeRadioUsage);
         dest.writeBoolean(mMaximizeDoze);
-        dest.writeInt(mUserModifiedFields);
     }
 
     /** Builder class for {@link ZenDeviceEffects} objects. */
@@ -365,7 +338,6 @@
         private boolean mDisableTouch;
         private boolean mMinimizeRadioUsage;
         private boolean mMaximizeDoze;
-        private @ModifiableField int mUserModifiedFields;
 
         /**
          * Instantiates a new {@link ZenPolicy.Builder} with all effects set to default (disabled).
@@ -388,7 +360,6 @@
             mDisableTouch = zenDeviceEffects.shouldDisableTouch();
             mMinimizeRadioUsage = zenDeviceEffects.shouldMinimizeRadioUsage();
             mMaximizeDoze = zenDeviceEffects.shouldMaximizeDoze();
-            mUserModifiedFields = zenDeviceEffects.mUserModifiedFields;
         }
 
         /**
@@ -510,24 +481,13 @@
             return this;
         }
 
-        /**
-         * Sets the bitmask representing which fields are user modified. See the FIELD_ constants.
-         * @hide
-         */
-        @TestApi
-        @NonNull
-        public Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
-            mUserModifiedFields = userModifiedFields;
-            return this;
-        }
-
         /** Builds a {@link ZenDeviceEffects} object based on the builder's state. */
         @NonNull
         public ZenDeviceEffects build() {
             return new ZenDeviceEffects(mGrayscale,
                     mSuppressAmbientDisplay, mDimWallpaper, mNightMode, mDisableAutoBrightness,
                     mDisableTapToWake, mDisableTiltToWake, mDisableTouch, mMinimizeRadioUsage,
-                    mMaximizeDoze, mUserModifiedFields);
+                    mMaximizeDoze);
         }
     }
 }
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d4d602d..c479877 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -206,7 +206,7 @@
     private static final String ALLOW_ATT_CONV = "convos";
     private static final String ALLOW_ATT_CONV_FROM = "convosFrom";
     private static final String ALLOW_ATT_CHANNELS = "priorityChannels";
-    private static final String USER_MODIFIED_FIELDS = "policyUserModifiedFields";
+    private static final String POLICY_USER_MODIFIED_FIELDS = "policyUserModifiedFields";
     private static final String DISALLOW_TAG = "disallow";
     private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
     private static final String STATE_TAG = "state";
@@ -806,6 +806,9 @@
             rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
             rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN);
             rt.userModifiedFields = safeInt(parser, RULE_ATT_USER_MODIFIED_FIELDS, 0);
+            rt.zenPolicyUserModifiedFields = safeInt(parser, POLICY_USER_MODIFIED_FIELDS, 0);
+            rt.zenDeviceEffectsUserModifiedFields = safeInt(parser,
+                    DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0);
             Long deletionInstant = tryParseLong(
                     parser.getAttributeValue(null, RULE_ATT_DELETION_INSTANT), null);
             if (deletionInstant != null) {
@@ -858,6 +861,9 @@
             }
             out.attributeInt(null, RULE_ATT_TYPE, rule.type);
             out.attributeInt(null, RULE_ATT_USER_MODIFIED_FIELDS, rule.userModifiedFields);
+            out.attributeInt(null, POLICY_USER_MODIFIED_FIELDS, rule.zenPolicyUserModifiedFields);
+            out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS,
+                    rule.zenDeviceEffectsUserModifiedFields);
             if (rule.deletionInstant != null) {
                 out.attributeLong(null, RULE_ATT_DELETION_INSTANT,
                         rule.deletionInstant.toEpochMilli());
@@ -924,7 +930,6 @@
                 builder.allowPriorityChannels(channels == ZenPolicy.STATE_ALLOW);
                 policySet = true;
             }
-            builder.setUserModifiedFields(safeInt(parser, USER_MODIFIED_FIELDS, 0));
         }
 
         if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
@@ -1037,7 +1042,6 @@
 
         if (Flags.modesApi()) {
             writeZenPolicyState(ALLOW_ATT_CHANNELS, policy.getPriorityChannels(), out);
-            out.attributeInt(null, USER_MODIFIED_FIELDS, policy.getUserModifiedFields());
         }
     }
 
@@ -1083,7 +1087,6 @@
                 .setShouldMinimizeRadioUsage(
                         safeBoolean(parser, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE, false))
                 .setShouldMaximizeDoze(safeBoolean(parser, DEVICE_EFFECT_MAXIMIZE_DOZE, false))
-                .setUserModifiedFields(safeInt(parser, DEVICE_EFFECT_USER_MODIFIED_FIELDS, 0))
                 .build();
 
         return deviceEffects.hasEffects() ? deviceEffects : null;
@@ -1108,8 +1111,6 @@
         writeBooleanIfTrue(out, DEVICE_EFFECT_MINIMIZE_RADIO_USAGE,
                 deviceEffects.shouldMinimizeRadioUsage());
         writeBooleanIfTrue(out, DEVICE_EFFECT_MAXIMIZE_DOZE, deviceEffects.shouldMaximizeDoze());
-        out.attributeInt(null, DEVICE_EFFECT_USER_MODIFIED_FIELDS,
-                deviceEffects.getUserModifiedFields());
     }
 
     private static void writeBooleanIfTrue(TypedXmlSerializer out, String att, boolean value)
@@ -2041,7 +2042,9 @@
         public String triggerDescription;
         public String iconResName;
         public boolean allowManualInvocation;
-        public int userModifiedFields;
+        @AutomaticZenRule.ModifiableField public int userModifiedFields;
+        @ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields;
+        @ZenDeviceEffects.ModifiableField public int zenDeviceEffectsUserModifiedFields;
         @Nullable public Instant deletionInstant; // Only set on deleted rules.
 
         public ZenRule() { }
@@ -2076,6 +2079,8 @@
                 triggerDescription = source.readString();
                 type = source.readInt();
                 userModifiedFields = source.readInt();
+                zenPolicyUserModifiedFields = source.readInt();
+                zenDeviceEffectsUserModifiedFields = source.readInt();
                 if (source.readInt() == 1) {
                     deletionInstant = Instant.ofEpochMilli(source.readLong());
                 }
@@ -2083,15 +2088,21 @@
         }
 
         /**
-         * @see AutomaticZenRule#canUpdate()
+         * Whether this ZenRule can be updated by an app. In general, rules that have been
+         * customized by the user cannot be further updated by an app, with some exceptions:
+         * <ul>
+         *     <li>Non user-configurable fields, like type, icon, configurationActivity, etc.
+         *     <li>Name, if the name was not specifically modified by the user (to support language
+         *          switches).
+         * </ul>
          */
         @FlaggedApi(Flags.FLAG_MODES_API)
         public boolean canBeUpdatedByApp() {
             // The rule is considered updateable if its bitmask has no user modifications, and
             // the bitmasks of the policy and device effects have no modification.
             return userModifiedFields == 0
-                    && (zenPolicy == null || zenPolicy.getUserModifiedFields() == 0)
-                    && (zenDeviceEffects == null || zenDeviceEffects.getUserModifiedFields() == 0);
+                    && zenPolicyUserModifiedFields == 0
+                    && zenDeviceEffectsUserModifiedFields == 0;
         }
 
         @Override
@@ -2139,6 +2150,8 @@
                 dest.writeString(triggerDescription);
                 dest.writeInt(type);
                 dest.writeInt(userModifiedFields);
+                dest.writeInt(zenPolicyUserModifiedFields);
+                dest.writeInt(zenDeviceEffectsUserModifiedFields);
                 if (deletionInstant != null) {
                     dest.writeInt(1);
                     dest.writeLong(deletionInstant.toEpochMilli());
@@ -2173,8 +2186,20 @@
                         .append(",allowManualInvocation=").append(allowManualInvocation)
                         .append(",iconResName=").append(iconResName)
                         .append(",triggerDescription=").append(triggerDescription)
-                        .append(",type=").append(type)
-                        .append(",userModifiedFields=").append(userModifiedFields);
+                        .append(",type=").append(type);
+                if (userModifiedFields != 0) {
+                    sb.append(",userModifiedFields=")
+                            .append(AutomaticZenRule.fieldsToString(userModifiedFields));
+                }
+                if (zenPolicyUserModifiedFields != 0) {
+                    sb.append(",zenPolicyUserModifiedFields=")
+                            .append(ZenPolicy.fieldsToString(zenPolicyUserModifiedFields));
+                }
+                if (zenDeviceEffectsUserModifiedFields != 0) {
+                    sb.append(",zenDeviceEffectsUserModifiedFields=")
+                            .append(ZenDeviceEffects.fieldsToString(
+                                    zenDeviceEffectsUserModifiedFields));
+                }
                 if (deletionInstant != null) {
                     sb.append(",deletionInstant=").append(deletionInstant);
                 }
@@ -2238,6 +2263,9 @@
                         && Objects.equals(other.triggerDescription, triggerDescription)
                         && other.type == type
                         && other.userModifiedFields == userModifiedFields
+                        && other.zenPolicyUserModifiedFields == zenPolicyUserModifiedFields
+                        && other.zenDeviceEffectsUserModifiedFields
+                            == zenDeviceEffectsUserModifiedFields
                         && Objects.equals(other.deletionInstant, deletionInstant);
             }
 
@@ -2250,7 +2278,8 @@
                 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                         component, configurationActivity, pkg, id, enabler, zenPolicy,
                         zenDeviceEffects, modified, allowManualInvocation, iconResName,
-                        triggerDescription, type, userModifiedFields, deletionInstant);
+                        triggerDescription, type, userModifiedFields, zenPolicyUserModifiedFields,
+                        zenDeviceEffectsUserModifiedFields, deletionInstant);
             }
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                     component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 2cda7c8..fb491d0 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -45,8 +45,8 @@
  */
 public final class ZenPolicy implements Parcelable {
 
-    /** Used to track which rule variables have been modified by the user.
-     * Should be checked against the bitmask {@link #getUserModifiedFields()}.
+    /**
+     * Enum for the user-modifiable fields in this object.
      * @hide
      */
     @IntDef(flag = true, prefix = { "FIELD_" }, value = {
@@ -76,7 +76,6 @@
      * the same time.
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_MESSAGES = 1 << 0;
     /**
@@ -84,7 +83,6 @@
      * the same time.
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_CALLS = 1 << 1;
     /**
@@ -92,13 +90,11 @@
      * set at the same time.
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_CONVERSATIONS = 1 << 2;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_ALLOW_CHANNELS = 1 << 3;
     /**
@@ -109,73 +105,61 @@
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 1 << 5;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 6;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 1 << 7;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 1 << 8;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 1 << 9;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1 << 10;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_LIGHTS = 1 << 11;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_PEEK = 1 << 12;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 1 << 13;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_BADGE = 1 << 14;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_AMBIENT = 1 << 15;
     /**
      * @hide
      */
-    @TestApi
     @FlaggedApi(Flags.FLAG_MODES_API)
     public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 1 << 16;
 
@@ -186,7 +170,6 @@
     private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
     @FlaggedApi(Flags.FLAG_MODES_API)
     private @ChannelType int mAllowChannels = CHANNEL_POLICY_UNSET;
-    private final @ModifiableField int mUserModifiedFields; // Bitwise representation
 
     /** @hide */
     @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
@@ -388,22 +371,19 @@
     public ZenPolicy() {
         mPriorityCategories = new ArrayList<>(Collections.nCopies(NUM_PRIORITY_CATEGORIES, 0));
         mVisualEffects = new ArrayList<>(Collections.nCopies(NUM_VISUAL_EFFECTS, 0));
-        mUserModifiedFields = 0;
     }
 
     /** @hide */
     @FlaggedApi(Flags.FLAG_MODES_API)
     public ZenPolicy(List<Integer> priorityCategories, List<Integer> visualEffects,
                      @PeopleType int priorityMessages, @PeopleType int priorityCalls,
-                     @ConversationSenders int conversationSenders, @ChannelType int allowChannels,
-                     @ModifiableField int userModifiedFields) {
+                     @ConversationSenders int conversationSenders, @ChannelType int allowChannels) {
         mPriorityCategories = priorityCategories;
         mVisualEffects = visualEffects;
         mPriorityMessages = priorityMessages;
         mPriorityCalls = priorityCalls;
         mConversationSenders = conversationSenders;
         mAllowChannels = allowChannels;
-        mUserModifiedFields = userModifiedFields;
     }
 
     /**
@@ -633,8 +613,6 @@
      * is not set, it is (@link STATE_UNSET} and will not change the current set policy.
      */
     public static final class Builder {
-        private @ModifiableField int mUserModifiedFields;
-
         private ZenPolicy mZenPolicy;
 
         public Builder() {
@@ -649,9 +627,6 @@
         public Builder(@Nullable ZenPolicy policy) {
             if (policy != null) {
                 mZenPolicy = policy.copy();
-                if (Flags.modesApi()) {
-                    mUserModifiedFields = policy.mUserModifiedFields;
-                }
             } else {
                 mZenPolicy = new ZenPolicy();
             }
@@ -662,11 +637,10 @@
          */
         public @NonNull ZenPolicy build() {
             if (Flags.modesApi()) {
-                return new ZenPolicy(new ArrayList<Integer>(mZenPolicy.mPriorityCategories),
-                        new ArrayList<Integer>(mZenPolicy.mVisualEffects),
+                return new ZenPolicy(new ArrayList<>(mZenPolicy.mPriorityCategories),
+                        new ArrayList<>(mZenPolicy.mVisualEffects),
                         mZenPolicy.mPriorityMessages, mZenPolicy.mPriorityCalls,
-                        mZenPolicy.mConversationSenders, mZenPolicy.mAllowChannels,
-                        mUserModifiedFields);
+                        mZenPolicy.mConversationSenders, mZenPolicy.mAllowChannels);
             } else {
                 return mZenPolicy.copy();
             }
@@ -1025,28 +999,6 @@
             mZenPolicy.mAllowChannels = allow ? CHANNEL_POLICY_PRIORITY : CHANNEL_POLICY_NONE;
             return this;
         }
-
-        /**
-         * Sets the user modified fields bitmask.
-         * @hide
-         */
-        @TestApi
-        @FlaggedApi(Flags.FLAG_MODES_API)
-        public @NonNull Builder setUserModifiedFields(@ModifiableField int userModifiedFields) {
-            mUserModifiedFields = userModifiedFields;
-            return this;
-        }
-    }
-
-    /**
-     Gets the bitmask representing which fields are user modified. Bits are set using
-     * {@link ModifiableField}.
-     * @hide
-     */
-    @TestApi
-    @FlaggedApi(Flags.FLAG_MODES_API)
-    public @ModifiableField int getUserModifiedFields() {
-        return mUserModifiedFields;
     }
 
     @Override
@@ -1063,7 +1015,6 @@
         dest.writeInt(mConversationSenders);
         if (Flags.modesApi()) {
             dest.writeInt(mAllowChannels);
-            dest.writeInt(mUserModifiedFields);
         }
     }
 
@@ -1079,7 +1030,7 @@
                                 trimList(source.readArrayList(Integer.class.getClassLoader(),
                                         Integer.class), NUM_VISUAL_EFFECTS),
                                 source.readInt(), source.readInt(), source.readInt(),
-                                source.readInt(), source.readInt()
+                                source.readInt()
                         );
                     } else {
                         policy = new ZenPolicy();
@@ -1114,14 +1065,12 @@
                         conversationTypeToString(mConversationSenders));
         if (Flags.modesApi()) {
             sb.append(", allowChannels=").append(channelTypeToString(mAllowChannels));
-            sb.append(", userModifiedFields=")
-                    .append(modifiedFieldsToString(mUserModifiedFields));
         }
         return sb.append('}').toString();
     }
 
-    @FlaggedApi(Flags.FLAG_MODES_API)
-    private String modifiedFieldsToString(@ModifiableField int bitmask) {
+    /** @hide */
+    public static String fieldsToString(@ModifiableField int bitmask) {
         ArrayList<String> modified = new ArrayList<>();
         if ((bitmask & FIELD_MESSAGES) != 0) {
             modified.add("FIELD_MESSAGES");
@@ -1332,8 +1281,7 @@
                 && other.mPriorityMessages == mPriorityMessages
                 && other.mConversationSenders == mConversationSenders;
         if (Flags.modesApi()) {
-            return eq && other.mAllowChannels == mAllowChannels
-                    && other.mUserModifiedFields == mUserModifiedFields;
+            return eq && other.mAllowChannels == mAllowChannels;
         }
         return eq;
     }
@@ -1342,7 +1290,7 @@
     public int hashCode() {
         if (Flags.modesApi()) {
             return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls,
-                    mPriorityMessages, mConversationSenders, mAllowChannels, mUserModifiedFields);
+                    mPriorityMessages, mConversationSenders, mAllowChannels);
         }
         return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
                 mConversationSenders);
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index adc54f5..f2bdbf6 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -325,7 +325,7 @@
             Slog.v(TAG, "BinderCallback#onQueryDetected");
             Binder.withCleanCallingIdentity(() -> {
                 synchronized (mLock) {
-                    mCallback.onQueryDetected(partialQuery);
+                    mExecutor.execute(()->mCallback.onQueryDetected(partialQuery));
                 }
             });
         }
@@ -335,7 +335,7 @@
             Slog.v(TAG, "BinderCallback#onQueryFinished");
             Binder.withCleanCallingIdentity(() -> {
                 synchronized (mLock) {
-                    mCallback.onQueryFinished();
+                    mExecutor.execute(()->mCallback.onQueryFinished());
                 }
             });
         }
@@ -345,7 +345,7 @@
             Slog.v(TAG, "BinderCallback#onQueryRejected");
             Binder.withCleanCallingIdentity(() -> {
                 synchronized (mLock) {
-                    mCallback.onQueryRejected();
+                    mExecutor.execute(()->mCallback.onQueryRejected());
                 }
             });
         }
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index cf1156d..fb57921 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -1681,6 +1681,10 @@
                 @EmergencyCallbackModeStopReason int reason) {
             // not support. Can't override. Use TelephonyCallback.
         }
+
+        public final void onSimultaneousCallingStateChanged(int[] subIds) {
+            // not supported on the deprecated interface - Use TelephonyCallback instead
+        }
     }
 
     private void log(String s) {
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 19bcf28..dc6a035 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
@@ -33,15 +34,19 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IPhoneStateListener;
+import com.android.internal.telephony.flags.Flags;
 
 import dalvik.system.VMRuntime;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
  * A callback class for monitoring changes in specific telephony states
@@ -627,6 +632,18 @@
     public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40;
 
     /**
+     * Event for listening to changes in simultaneous cellular calling subscriptions.
+     *
+     * @see SimultaneousCellularCallingSupportListener
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @SystemApi
+    public static final int EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED = 41;
+
+    /**
      * @hide
      */
     @IntDef(prefix = {"EVENT_"}, value = {
@@ -669,7 +686,8 @@
             EVENT_LINK_CAPACITY_ESTIMATE_CHANGED,
             EVENT_TRIGGER_NOTIFY_ANBR,
             EVENT_MEDIA_QUALITY_STATUS_CHANGED,
-            EVENT_EMERGENCY_CALLBACK_MODE_CHANGED
+            EVENT_EMERGENCY_CALLBACK_MODE_CHANGED,
+            EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface TelephonyEvent {
@@ -1373,6 +1391,44 @@
     }
 
     /**
+     * Interface for listening to changes in the simultaneous cellular calling state for active
+     * cellular subscriptions.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    @SystemApi
+    public interface SimultaneousCellularCallingSupportListener {
+        /**
+         * Notify the Listener that the subscriptions available for simultaneous <b>cellular</b>
+         * calling have changed.
+         * <p>
+         * If we have an ongoing <b>cellular</b> call on one subscription in this Set, a
+         * simultaneous incoming or outgoing <b>cellular</b> call is possible on any of the
+         * subscriptions in this Set. On a traditional Dual Sim Dual Standby device, simultaneous
+         * calling is not possible between subscriptions, where on a Dual Sim Dual Active device,
+         * simultaneous calling may be possible between subscriptions in certain network conditions.
+         * <p>
+         * Note: This listener only tracks the capability of the modem to perform simultaneous
+         * cellular calls and does not track the simultaneous calling state of scenarios based on
+         * multiple IMS registration over multiple transports (WiFi/Internet calling).
+         * <p>
+         * Note: This listener fires for all changes to cellular calling subscriptions independent
+         * of which subscription it is registered on.
+         *
+         * @param simultaneousCallingSubscriptionIds The Set of subscription IDs that support
+         * simultaneous calling. If there is an ongoing call on a subscription in this Set, then a
+         * simultaneous incoming or outgoing call is only possible for other subscriptions in this
+         * Set. If there is an ongoing call on a subscription that is not in this Set, then
+         * simultaneous calling is not possible at the current time.
+         *
+         */
+        @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+        void onSimultaneousCellularCallingSubscriptionsChanged(
+                @NonNull Set<Integer> simultaneousCallingSubscriptionIds);
+    }
+
+    /**
      * Interface for call attributes listener.
      *
      * @hide
@@ -1976,6 +2032,17 @@
                                     allowedNetworkType)));
         }
 
+        public void onSimultaneousCallingStateChanged(int[] subIds) {
+            SimultaneousCellularCallingSupportListener listener =
+                    (SimultaneousCellularCallingSupportListener) mTelephonyCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> listener.onSimultaneousCellularCallingSubscriptionsChanged(
+                                    Arrays.stream(subIds).boxed().collect(Collectors.toSet()))));
+        }
+
         public void onLinkCapacityEstimateChanged(
                 List<LinkCapacityEstimate> linkCapacityEstimateList) {
             LinkCapacityEstimateChangedListener listener =
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 886727e..0de4505 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -994,6 +994,21 @@
         }
     }
 
+    /**
+     * Notify external listeners that the subscriptions supporting simultaneous cellular calling
+     * have changed.
+     * @param subIds The new set of subIds supporting simultaneous cellular calling.
+     */
+    public void notifySimultaneousCellularCallingSubscriptionsChanged(Set<Integer> subIds) {
+        try {
+            sRegistry.notifySimultaneousCellularCallingSubscriptionsChanged(
+                    subIds.stream().mapToInt(i -> i).toArray());
+        } catch (RemoteException ex) {
+            // system server crash
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     public @NonNull Set<Integer> getEventsFromCallback(
             @NonNull TelephonyCallback telephonyCallback) {
         Set<Integer> eventList = new ArraySet<>();
@@ -1135,7 +1150,11 @@
             eventList.add(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
         }
 
-
+        if (telephonyCallback
+                instanceof TelephonyCallback.SimultaneousCellularCallingSupportListener) {
+            eventList.add(
+                    TelephonyCallback.EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);
+        }
         return eventList;
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 257ecc5..c98d1d7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15015,6 +15015,7 @@
     }
 
     /** @hide */
+    @Nullable
     View getSelfOrParentImportantForA11y() {
         if (isImportantForAccessibility()) return this;
         ViewParent parent = getParentForAccessibility();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c66f3c8..57174de 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -26,6 +26,7 @@
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
@@ -87,6 +88,7 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+import static android.view.accessibility.Flags.fixMergedContentChangeEvent;
 import static android.view.accessibility.Flags.forceInvertColor;
 import static android.view.accessibility.Flags.reduceWindowContentChangedEventThrottle;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
@@ -1009,8 +1011,10 @@
     // Used to check if there were any view invalidations in
     // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
     private boolean mHasInvalidation = false;
-    // Used to check if it is in the touch boosting period.
+    // Used to check if it is in the frame rate boosting period.
     private boolean mIsFrameRateBoosting = false;
+    // Used to check if it is in touch boosting period.
+    private boolean mIsTouchBoosting = false;
     // Used to check if there is a message in the message queue
     // for idleness handling.
     private boolean mHasIdledMessage = false;
@@ -6425,11 +6429,12 @@
                      * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
                      */
                     mIsFrameRateBoosting = false;
+                    mIsTouchBoosting = false;
                     setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
                             mLastPreferredFrameRateCategory));
                     break;
                 case MSG_CHECK_INVALIDATION_IDLE:
-                    if (!mHasInvalidation && !mIsFrameRateBoosting) {
+                    if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) {
                         mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                         setPreferredFrameRateCategory(mPreferredFrameRateCategory);
                         mHasIdledMessage = false;
@@ -7452,7 +7457,7 @@
             // For the variable refresh rate project
             if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
                 // set the frame rate to the maximum value.
-                mIsFrameRateBoosting = true;
+                mIsTouchBoosting = true;
                 setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             }
             /**
@@ -11509,6 +11514,15 @@
                 event.setContentChangeTypes(mChangeTypes);
                 if (mAction.isPresent()) event.setAction(mAction.getAsInt());
                 if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin;
+
+                if (fixMergedContentChangeEvent()) {
+                    if ((mChangeTypes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+                        final View importantParent = source.getSelfOrParentImportantForA11y();
+                        if (importantParent != null) {
+                            source = importantParent;
+                        }
+                    }
+                }
                 source.sendAccessibilityEventUnchecked(event);
             } else {
                 mLastEventTimeMillis = 0;
@@ -11543,14 +11557,29 @@
             }
 
             if (mSource != null) {
-                // If there is no common predecessor, then mSource points to
-                // a removed view, hence in this case always prefer the source.
-                View predecessor = getCommonPredecessor(mSource, source);
-                if (predecessor != null) {
-                    predecessor = predecessor.getSelfOrParentImportantForA11y();
+                if (fixMergedContentChangeEvent()) {
+                    View newSource = getCommonPredecessor(mSource, source);
+                    if (newSource == null) {
+                        // If there is no common predecessor, then mSource points to
+                        // a removed view, hence in this case always prefer the source.
+                        newSource = source;
+                    }
+
+                    mChangeTypes |= changeType;
+                    if (mSource != newSource) {
+                        mChangeTypes |= AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
+                        mSource = newSource;
+                    }
+                } else {
+                    // If there is no common predecessor, then mSource points to
+                    // a removed view, hence in this case always prefer the source.
+                    View predecessor = getCommonPredecessor(mSource, source);
+                    if (predecessor != null) {
+                        predecessor = predecessor.getSelfOrParentImportantForA11y();
+                    }
+                    mSource = (predecessor != null) ? predecessor : source;
+                    mChangeTypes |= changeType;
                 }
-                mSource = (predecessor != null) ? predecessor : source;
-                mChangeTypes |= changeType;
 
                 final int performingAction = mAccessibilityManager.getPerformingAction();
                 if (performingAction != 0) {
@@ -12177,8 +12206,16 @@
             return;
         }
 
-        int frameRateCategory = mIsFrameRateBoosting || mInsetsAnimationRunning
-                ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;
+        int frameRateCategory = mIsTouchBoosting
+                ? FRAME_RATE_CATEGORY_HIGH_HINT : preferredFrameRateCategory;
+
+        // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT
+        // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction.
+        // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction
+        // (e.g., Window Initialization).
+        if (mIsFrameRateBoosting || mInsetsAnimationRunning) {
+            frameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+        }
 
         try {
             if (mLastPreferredFrameRateCategory != frameRateCategory) {
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index c7355c1..efae57c 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -53,6 +53,13 @@
 
 flag {
     namespace: "accessibility"
+    name: "fix_merged_content_change_event"
+    description: "Fixes event type and source of content change event merged in ViewRootImpl"
+    bug: "277305460"
+}
+
+flag {
+    namespace: "accessibility"
     name: "flash_notification_system_api"
     description: "Makes flash notification APIs as system APIs for calling from mainline module"
     bug: "303131332"
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index bceb872..feae173 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -421,8 +421,11 @@
         final String perChangeLineStart = shouldPrettyPrint ? "\n" + innerPrefix : "";
         StringBuilder sb = new StringBuilder();
         sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
-                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack)
-                .append(" r=[");
+                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack);
+        if (mOptions != null) {
+            sb.append(" opt=").append(mOptions);
+        }
+        sb.append(" r=[");
         for (int i = 0; i < mRoots.size(); ++i) {
             if (i > 0) {
                 sb.append(',');
@@ -1211,21 +1214,31 @@
 
         @NonNull
         private static String typeToString(int mode) {
-            switch(mode) {
-                case ANIM_CUSTOM: return "ANIM_CUSTOM";
-                case ANIM_CLIP_REVEAL: return "ANIM_CLIP_REVEAL";
-                case ANIM_SCALE_UP: return "ANIM_SCALE_UP";
-                case ANIM_THUMBNAIL_SCALE_UP: return "ANIM_THUMBNAIL_SCALE_UP";
-                case ANIM_THUMBNAIL_SCALE_DOWN: return "ANIM_THUMBNAIL_SCALE_DOWN";
-                case ANIM_OPEN_CROSS_PROFILE_APPS: return "ANIM_OPEN_CROSS_PROFILE_APPS";
-                default: return "<unknown:" + mode + ">";
-            }
+            return switch (mode) {
+                case ANIM_CUSTOM -> "CUSTOM";
+                case ANIM_SCALE_UP -> "SCALE_UP";
+                case ANIM_THUMBNAIL_SCALE_UP -> "THUMBNAIL_SCALE_UP";
+                case ANIM_THUMBNAIL_SCALE_DOWN -> "THUMBNAIL_SCALE_DOWN";
+                case ANIM_SCENE_TRANSITION -> "SCENE_TRANSITION";
+                case ANIM_CLIP_REVEAL -> "CLIP_REVEAL";
+                case ANIM_OPEN_CROSS_PROFILE_APPS -> "OPEN_CROSS_PROFILE_APPS";
+                case ANIM_FROM_STYLE -> "FROM_STYLE";
+                default -> "<" + mode + ">";
+            };
         }
 
         @Override
         public String toString() {
-            return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName
-                    + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
+            final StringBuilder sb = new StringBuilder(32);
+            sb.append("{t=").append(typeToString(mType));
+            if (mOverrideTaskTransition) {
+                sb.append(" overrideTask=true");
+            }
+            if (!mTransitionBounds.isEmpty()) {
+                sb.append(" bounds=").append(mTransitionBounds);
+            }
+            sb.append('}');
+            return sb.toString();
         }
 
         /** Customized activity transition. */
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index bb16ad2..2b96ee6 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -69,11 +69,10 @@
 }
 
 flag {
-    name: "predictive_back_system_animations"
+    name: "predictive_back_system_anims"
     namespace: "systemui"
     description: "Predictive back for system animations"
-    bug: "319421778"
-    is_fixed_read_only: true
+    bug: "320510464"
 }
 
 flag {
diff --git a/core/java/com/android/internal/os/MonotonicClock.java b/core/java/com/android/internal/os/MonotonicClock.java
index c3bcfa6..eca6f58 100644
--- a/core/java/com/android/internal/os/MonotonicClock.java
+++ b/core/java/com/android/internal/os/MonotonicClock.java
@@ -17,11 +17,11 @@
 package com.android.internal.os;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Xml;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
@@ -49,20 +49,27 @@
 
     private final AtomicFile mFile;
     private final Clock mClock;
-    private long mTimeshift;
+    private final long mTimeshift;
 
     public static final long UNDEFINED = -1;
 
     public MonotonicClock(File file) {
-        mFile = new AtomicFile(file);
-        mClock = Clock.SYSTEM_CLOCK;
-        read();
+        this (file, Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK);
     }
 
     public MonotonicClock(long monotonicTime, @NonNull Clock clock) {
+        this(null, monotonicTime, clock);
+    }
+
+    public MonotonicClock(@Nullable File file, long monotonicTime, @NonNull Clock clock) {
         mClock = clock;
-        mFile = null;
-        mTimeshift = monotonicTime - mClock.elapsedRealtime();
+        if (file != null) {
+            mFile = new AtomicFile(file);
+            mTimeshift = read(monotonicTime - mClock.elapsedRealtime());
+        } else {
+            mFile = null;
+            mTimeshift = monotonicTime - mClock.elapsedRealtime();
+        }
     }
 
     /**
@@ -81,15 +88,16 @@
         return mTimeshift + elapsedRealtimeMs;
     }
 
-    private void read() {
+    private long read(long defaultTimeshift) {
         if (!mFile.exists()) {
-            return;
+            return defaultTimeshift;
         }
 
         try {
-            readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser());
+            return readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser());
         } catch (IOException e) {
             Log.e(TAG, "Cannot load monotonic clock from " + mFile.getBaseFile(), e);
+            return defaultTimeshift;
         }
     }
 
@@ -102,18 +110,21 @@
             return;
         }
 
-        try (FileOutputStream out = mFile.startWrite()) {
+        FileOutputStream out = null;
+        try  {
+            out = mFile.startWrite();
             writeXml(out, Xml.newBinarySerializer());
+            mFile.finishWrite(out);
         } catch (IOException e) {
             Log.e(TAG, "Cannot write monotonic clock to " + mFile.getBaseFile(), e);
+            mFile.failWrite(out);
         }
     }
 
     /**
      * Parses an XML file containing the persistent state of the monotonic clock.
      */
-    @VisibleForTesting
-    public void readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException {
+    private long readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException {
         long savedTimeshift = 0;
         try {
             parser.setInput(inputStream, StandardCharsets.UTF_8.name());
@@ -128,14 +139,13 @@
         } catch (XmlPullParserException e) {
             throw new IOException(e);
         }
-        mTimeshift = savedTimeshift - mClock.elapsedRealtime();
+        return savedTimeshift - mClock.elapsedRealtime();
     }
 
     /**
      * Creates an XML file containing the persistent state of the monotonic clock.
      */
-    @VisibleForTesting
-    public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
+    private void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
         serializer.setOutput(out, StandardCharsets.UTF_8.name());
         serializer.startDocument(null, true);
         serializer.startTag(null, XML_TAG_MONOTONIC_TIME);
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 03cfd4f..969f95d 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -80,4 +80,5 @@
     void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus);
     void onCallBackModeStarted(int type);
     void onCallBackModeStopped(int type, int reason);
+    void onSimultaneousCallingStateChanged(in int[] subIds);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index aab2242..0203ea4 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -104,6 +104,7 @@
     void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
     void notifyLinkCapacityEstimateChanged(in int phoneId, in int subId,
             in List<LinkCapacityEstimate> linkCapacityEstimateList);
+    void notifySimultaneousCellularCallingSubscriptionsChanged(in int[] subIds);
 
     void addCarrierPrivilegesCallback(
             int phoneId, ICarrierPrivilegesCallback callback, String pkg, String featureId);
diff --git a/core/java/com/android/internal/util/RingBuffer.java b/core/java/com/android/internal/util/RingBuffer.java
index 0488659..342ba1b6 100644
--- a/core/java/com/android/internal/util/RingBuffer.java
+++ b/core/java/com/android/internal/util/RingBuffer.java
@@ -19,7 +19,10 @@
 import static com.android.internal.util.Preconditions.checkArgumentPositive;
 
 import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Arrays;
+import java.util.function.IntFunction;
+import java.util.function.Supplier;
 
 /**
  * A simple ring buffer structure with bounded capacity backed by an array.
@@ -30,16 +33,35 @@
 @android.ravenwood.annotation.RavenwoodKeepWholeClass
 public class RingBuffer<T> {
 
+    private final Supplier<T> mNewItem;
     // Array for storing events.
     private final T[] mBuffer;
     // Cursor keeping track of the logical end of the array. This cursor never
     // wraps and instead keeps track of the total number of append() operations.
     private long mCursor = 0;
 
+    /**
+     * @deprecated This uses reflection to create new instances.
+     *             Use {@link #RingBuffer(Supplier, IntFunction, int)}} instead.
+     */
+    @Deprecated
     public RingBuffer(Class<T> c, int capacity) {
+        this(() -> (T) createNewItem(c), cap -> (T[]) Array.newInstance(c, cap), capacity);
+    }
+
+    private static Object createNewItem(Class c) {
+        try {
+            return c.getDeclaredConstructor().newInstance();
+        } catch (IllegalAccessException | InstantiationException | NoSuchMethodException
+                 | InvocationTargetException e) {
+            return null;
+        }
+    }
+
+    public RingBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity) {
         checkArgumentPositive(capacity, "A RingBuffer cannot have 0 capacity");
-        // Java cannot create generic arrays without a runtime hint.
-        mBuffer = (T[]) Array.newInstance(c, capacity);
+        mBuffer = newBacking.apply(capacity);
+        mNewItem = newItem;
     }
 
     public int size() {
@@ -69,22 +91,11 @@
     public T getNextSlot() {
         final int nextSlotIdx = indexOf(mCursor++);
         if (mBuffer[nextSlotIdx] == null) {
-            mBuffer[nextSlotIdx] = createNewItem();
+            mBuffer[nextSlotIdx] = mNewItem.get();
         }
         return mBuffer[nextSlotIdx];
     }
 
-    /**
-     * @return a new object of type <T> or null if a new object could not be created.
-     */
-    protected T createNewItem() {
-        try {
-            return (T) mBuffer.getClass().getComponentType().newInstance();
-        } catch (IllegalAccessException | InstantiationException e) {
-            return null;
-        }
-    }
-
     public T[] toArray() {
         // Only generic way to create a T[] from another T[]
         T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass());
diff --git a/core/jni/android_hardware_OverlayProperties.cpp b/core/jni/android_hardware_OverlayProperties.cpp
index 5b95ee7..f6fe3dd 100644
--- a/core/jni/android_hardware_OverlayProperties.cpp
+++ b/core/jni/android_hardware_OverlayProperties.cpp
@@ -61,13 +61,12 @@
                           static_cast<int32_t>(HAL_PIXEL_FORMAT_RGBA_FP16)) !=
                         i.pixelFormats.end() &&
                 std::find(i.standards.begin(), i.standards.end(),
-                          static_cast<int32_t>(HAL_DATASPACE_STANDARD_BT2020)) !=
+                          static_cast<int32_t>(HAL_DATASPACE_STANDARD_BT709)) !=
                         i.standards.end() &&
                 std::find(i.transfers.begin(), i.transfers.end(),
-                          static_cast<int32_t>(HAL_DATASPACE_TRANSFER_ST2084)) !=
-                        i.transfers.end() &&
+                          static_cast<int32_t>(HAL_DATASPACE_TRANSFER_SRGB)) != i.transfers.end() &&
                 std::find(i.ranges.begin(), i.ranges.end(),
-                          static_cast<int32_t>(HAL_DATASPACE_RANGE_FULL)) != i.ranges.end()) {
+                          static_cast<int32_t>(HAL_DATASPACE_RANGE_EXTENDED)) != i.ranges.end()) {
                 return true;
             }
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5f3f641..4a82675 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7896,6 +7896,18 @@
     <permission android:name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS"
         android:protectionLevel="signature|role" />
 
+    <!-- @SystemApi
+         @FlaggedApi("android.app.bic_client")
+         Allows app to call BackgroundInstallControlManager API to retrieve silently installed apps
+         for all users on device.
+         <p>Apps with a BackgroundInstallControlManager client will not be able to call any API without
+         this permission.
+         <p>Protection level: signature|role
+         @hide
+     -->
+    <permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES"
+        android:protectionLevel="signature|role" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index f3acab0..fe12f6e 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,5 +1,5 @@
 <!--
-Copyright (C) 2021 The Android Open Source Project
+Copyright (C) 2024 The Android Open Source Project
 
    Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -20,179 +20,103 @@
     android:viewportWidth="512"
     android:viewportHeight="512">
   <path
-      android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0">
+      android:pathData="M256.22,437.7c-26.38,0 -43.81,-18.3 -61.2,-48.42 -17.39,-30.12 -103.26,-178.86 -120.65,-208.98s-24.52,-54.37 -11.33,-77.21c13.19,-22.84 37.75,-28.79 72.53,-28.79 34.78,0 206.53,0 241.31,0 34.78,0 59.35,5.95 72.53,28.79 13.19,22.84 6.06,47.09 -11.33,77.21s-103.26,178.86 -120.65,208.98c-17.39,30.12 -34.83,48.42 -61.2,48.42Z"
+      android:strokeWidth="0">
     <aapt:attr name="android:fillColor">
       <gradient 
-          android:startX="256"
-          android:startY="21.81"
-          android:endX="256"
-          android:endY="350.42"
+          android:startX="56.22"
+          android:startY="256"
+          android:endX="456.22"
+          android:endY="256"
           android:type="linear">
         <item android:offset="0" android:color="#FF073042"/>
         <item android:offset="1" android:color="#FF073042"/>
       </gradient>
     </aapt:attr>
   </path>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z"
-        android:fillColor="#3ddc84"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z"
-        android:fillColor="#3ddc84"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z"
-        android:fillColor="#3ddc84"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M171.92,216.82h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M188.8,275.73h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M369.04,337.63h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M285.93,252.22h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M318.96,218.84h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M294.05,288.55h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M330.82,273.31h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M188.8,298.95h4.04v4.04h-4.04z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M220.14,238.94h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M272.1,318.9h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M293.34,349.25h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M161.05,254.24h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M378.92,192h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
-  <group>
-    <clip-path
-        android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/>
-    <path
-        android:pathData="M137.87,323.7h8.08v8.08h-8.08z"
-        android:fillColor="#fff"/>
-  </group>
   <path
-      android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"
-      android:strokeWidth="56.561"
-      android:fillColor="#00000000"
-      android:strokeColor="#f86734"/>
-  <path
-      android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z"
+      android:pathData="M198.85,168.57l2.03,-6.03c12.55,70.48 45.87,256.56 45.87,45.41 0,-0.88 0.65,-1.63 1.46,-1.63h11.45c0.81,0 1.46,0.75 1.46,1.63 -0.18,369.8 -62.28,-39.37 -62.28,-39.37Z"
+      android:strokeWidth="0"
       android:fillColor="#3ddc84"/>
   <path
-      android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z"
+      android:pathData="M186.69,167.97l-23.69,41.03c-1.36,2.36 -0.55,5.37 1.8,6.73 2.36,1.36 5.37,0.55 6.73,-1.8l23.99,-41.55c38.76,17.38 83.1,17.38 121.86,0l23.99,41.55c1.41,2.33 4.44,3.07 6.77,1.66 2.26,-1.37 3.04,-4.28 1.76,-6.59l-23.69,-41.03c40.68,-22.12 68.5,-63.31 72.57,-111.97H114.11c4.07,48.65 31.89,89.85 72.57,111.97Z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z"
+      android:pathData="M248.46,168.59L259.2,168.59A1.92,1.92 0,0 1,261.12 170.5L261.12,195.83A1.92,1.92 0,0 1,259.2 197.75L248.46,197.75A1.92,1.92 0,0 1,246.54 195.83L246.54,170.5A1.92,1.92 0,0 1,248.46 168.59z"
+      android:strokeWidth="0"
+      android:fillColor="#3ddc84"/>
+  <path
+      android:pathData="M248.32,152.92L259.34,152.92A1.78,1.78 0,0 1,261.12 154.7L261.12,158.43A1.78,1.78 0,0 1,259.34 160.21L248.32,160.21A1.78,1.78 0,0 1,246.54 158.43L246.54,154.7A1.78,1.78 0,0 1,248.32 152.92z"
+      android:strokeWidth="0"
+      android:fillColor="#3ddc84"/>
+  <path
+      android:pathData="M159.03,176.91h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z"
+      android:pathData="M188.8,275.73h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z"
+      android:pathData="M373.41,158.93h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z"
+      android:pathData="M112.1,129.34h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z"
+      android:pathData="M285.93,252.22h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z"
+      android:pathData="M318.96,218.84h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z"
+      android:pathData="M294.05,288.55h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
   <path
-      android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z"
+      android:pathData="M330.82,263.31h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
       android:fillColor="#fff"/>
+  <path
+      android:pathData="M188.8,298.95h4.04v4.04h-4.04z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M224.18,216.82h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M272.1,318.9h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M293.34,339.25h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M165.09,236.28h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M378.92,192h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M204.28,314.86h8.08v8.08h-8.08z"
+      android:strokeWidth="0"
+      android:fillColor="#fff"/>
+  <path
+      android:pathData="M253.83,118.47c-6.04,0 -10.93,4.76 -10.93,10.62v13.1c0,1.13 0.92,2.05 2.05,2.05h0c1.13,0 2.05,-0.92 2.05,-2.05v-3.53c0,-2.27 1.84,-4.1 4.1,-4.1h5.47c2.27,0 4.1,1.84 4.1,4.1v3.53c0,1.13 0.92,2.05 2.05,2.05s2.05,-0.92 2.05,-2.05v-13.1c0,-5.86 -4.9,-10.62 -10.93,-10.62Z"
+      android:strokeWidth="0"
+      android:fillColor="#3ddc84"/>
+  <path
+      android:pathData="M256,437.7c-26.38,0 -43.81,-18.3 -61.2,-48.42 -17.39,-30.12 -103.26,-178.86 -120.65,-208.98 -17.39,-30.12 -24.52,-54.37 -11.33,-77.21 13.19,-22.84 37.75,-28.79 72.53,-28.79 34.78,0 206.53,0 241.31,0 34.78,0 59.35,5.95 72.53,28.79 13.19,22.84 6.06,47.09 -11.33,77.21 -17.39,30.12 -103.26,178.86 -120.65,208.98 -17.39,30.12 -34.83,48.42 -61.2,48.42Z"
+      android:strokeWidth="55"
+      android:fillColor="#00000000"
+      android:strokeColor="#f86733"/>
 </vector>
-
diff --git a/core/tests/BroadcastRadioTests/TEST_MAPPING b/core/tests/BroadcastRadioTests/TEST_MAPPING
new file mode 100644
index 0000000..b085a27
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "BroadcastRadioTests"
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index 9d85b65..1925588 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -16,8 +16,6 @@
 
 package android.app;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.fail;
 
@@ -28,8 +26,6 @@
 import android.os.Parcel;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.service.notification.ZenDeviceEffects;
-import android.service.notification.ZenPolicy;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -230,66 +226,4 @@
 
         assertThrows(IllegalArgumentException.class, () -> builder.setType(100));
     }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    public void testCanUpdate_nullPolicyAndDeviceEffects() {
-        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
-                Uri.parse("uri://short"));
-
-        AutomaticZenRule rule = builder.setUserModifiedFields(0)
-                .setZenPolicy(null)
-                .setDeviceEffects(null)
-                .build();
-
-        assertThat(rule.canUpdate()).isTrue();
-
-        rule = builder.setUserModifiedFields(1).build();
-        assertThat(rule.canUpdate()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    public void testCanUpdate_policyModified() {
-        ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder().setUserModifiedFields(0);
-        ZenPolicy policy = policyBuilder.build();
-
-        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
-                Uri.parse("uri://short"));
-        AutomaticZenRule rule = builder.setUserModifiedFields(0)
-                .setZenPolicy(policy)
-                .setDeviceEffects(null).build();
-
-        // Newly created ZenPolicy is not user modified.
-        assertThat(policy.getUserModifiedFields()).isEqualTo(0);
-        assertThat(rule.canUpdate()).isTrue();
-
-        policy = policyBuilder.setUserModifiedFields(1).build();
-        assertThat(policy.getUserModifiedFields()).isEqualTo(1);
-        rule = builder.setZenPolicy(policy).build();
-        assertThat(rule.canUpdate()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    public void testCanUpdate_deviceEffectsModified() {
-        ZenDeviceEffects.Builder deviceEffectsBuilder =
-                new ZenDeviceEffects.Builder().setUserModifiedFields(0);
-        ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
-
-        AutomaticZenRule.Builder builder = new AutomaticZenRule.Builder("name",
-                Uri.parse("uri://short"));
-        AutomaticZenRule rule = builder.setUserModifiedFields(0)
-                .setZenPolicy(null)
-                .setDeviceEffects(deviceEffects).build();
-
-        // Newly created ZenDeviceEffects is not user modified.
-        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(0);
-        assertThat(rule.canUpdate()).isTrue();
-
-        deviceEffects = deviceEffectsBuilder.setUserModifiedFields(1).build();
-        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(1);
-        rule = builder.setDeviceEffects(deviceEffects).build();
-        assertThat(rule.canUpdate()).isFalse();
-    }
 }
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index 1617eda..e32a57b 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -48,7 +48,7 @@
     @get:Rule
     val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
 
-    private lateinit var defaultLookupTables: SparseArray<FontScaleConverter>
+    private var defaultLookupTables: SparseArray<FontScaleConverter>? = null
 
     @Before
     fun setup() {
@@ -58,7 +58,9 @@
     @After
     fun teardown() {
         // Restore the default tables (since some tests will have added extras to the cache)
-        FontScaleConverterFactory.sLookupTables = defaultLookupTables
+        if (defaultLookupTables != null) {
+            FontScaleConverterFactory.sLookupTables = defaultLookupTables!!
+        }
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index cf3eb12..cfbda84 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -19,6 +19,7 @@
 import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
 import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
 import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH_HINT;
 import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
 import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
@@ -575,8 +576,13 @@
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+                    FRAME_RATE_CATEGORY_HIGH_HINT);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH_HINT);
+            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
             assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
             viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
diff --git a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
index 0742052..ec4c563 100644
--- a/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java
@@ -18,39 +18,67 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.util.Xml;
-
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.file.Files;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MonotonicClockTest {
     private final MockClock mClock = new MockClock();
+    private File mFile;
+
+    @Before
+    public void setup() throws IOException {
+        File systemDir = Files.createTempDirectory("MonotonicClockTest").toFile();
+        mFile = new File(systemDir, "test_monotonic_clock.xml");
+        if (mFile.exists()) {
+            assertThat(mFile.delete()).isTrue();
+        }
+    }
 
     @Test
     public void persistence() throws IOException {
-        MonotonicClock monotonicClock = new MonotonicClock(1000, mClock);
+        MonotonicClock monotonicClock = new MonotonicClock(mFile, 1000, mClock);
         mClock.realtime = 234;
 
         assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
 
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        monotonicClock.writeXml(out, Xml.newBinarySerializer());
+        monotonicClock.write();
 
         mClock.realtime = 42;
-        MonotonicClock newMonotonicClock = new MonotonicClock(0, mClock);
-        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
-        newMonotonicClock.readXml(in, Xml.newBinaryPullParser());
+        MonotonicClock newMonotonicClock = new MonotonicClock(mFile, 0, mClock);
 
         mClock.realtime = 2000;
         assertThat(newMonotonicClock.monotonicTime()).isEqualTo(1234 - 42 + 2000);
     }
+
+    @Test
+    public void constructor() {
+        MonotonicClock monotonicClock = new MonotonicClock(null, 1000, mClock);
+        mClock.realtime = 234;
+
+        assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
+    }
+
+    @Test
+    public void corruptedFile() throws IOException {
+        // Create an invalid binary XML file to cause IOException: "Unexpected magic number"
+        try (FileWriter w = new FileWriter(mFile)) {
+            w.write("garbage");
+        }
+
+        MonotonicClock monotonicClock = new MonotonicClock(mFile, 1000, mClock);
+        mClock.realtime = 234;
+
+        assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
+    }
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a1ea2b8..4be75f8 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -426,6 +426,7 @@
         <!-- Permissions required for CTS test - android.server.biometrics -->
         <permission name="android.permission.USE_BIOMETRIC" />
         <permission name="android.permission.TEST_BIOMETRIC" />
+        <permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
         <!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
         <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
         <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases -->
@@ -541,6 +542,8 @@
         <permission name="android.permission.GET_BINDING_UID_IMPORTANCE"/>
         <!-- Permission required for CTS test NotificationManagerZenTest -->
         <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+        <!-- Permission required for BinaryTransparencyService shell API and host test -->
+        <permission name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index ddae673..b21bf11 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -23,9 +23,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.compat.CompatChanges;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
+import android.app.ActivityThread;
 import android.os.Build;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -43,15 +41,6 @@
  * line-break property</a> for more information.
  */
 public final class LineBreakConfig implements Parcelable {
-
-    /**
-     * A feature ID for automatic line break word style.
-     * @hide
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
-    public static final long WORD_STYLE_AUTO = 280005585L;
-
     /**
      * No hyphenation preference is specified.
      *
@@ -487,8 +476,15 @@
      * @hide
      */
     public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
-        final int defaultStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
-                ? LINE_BREAK_STYLE_AUTO : LINE_BREAK_STYLE_NONE;
+        final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
+                .targetSdkVersion;
+        final int defaultStyle;
+        final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        if (targetSdkVersion >= vicVersion) {
+            defaultStyle = LINE_BREAK_STYLE_AUTO;
+        } else {
+            defaultStyle = LINE_BREAK_STYLE_NONE;
+        }
         if (config == null) {
             return defaultStyle;
         }
@@ -515,8 +511,15 @@
      */
     public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
             @Nullable LineBreakConfig config) {
-        final int defaultWordStyle = CompatChanges.isChangeEnabled(WORD_STYLE_AUTO)
-                ? LINE_BREAK_WORD_STYLE_AUTO : LINE_BREAK_WORD_STYLE_NONE;
+        final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
+                .targetSdkVersion;
+        final int defaultWordStyle;
+        final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
+        if (targetSdkVersion >= vicVersion) {
+            defaultWordStyle = LINE_BREAK_WORD_STYLE_AUTO;
+        } else {
+            defaultWordStyle = LINE_BREAK_WORD_STYLE_NONE;
+        }
         if (config == null) {
             return defaultWordStyle;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index bb433db..e7f6f0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -17,7 +17,7 @@
 package com.android.wm.shell.back;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
-import static com.android.window.flags.Flags.predictiveBackSystemAnimations;
+import static com.android.window.flags.Flags.predictiveBackSystemAnims;
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
@@ -244,7 +244,7 @@
     private void setupAnimationDeveloperSettingsObserver(
             @NonNull ContentResolver contentResolver,
             @NonNull @ShellBackgroundThread final Handler backgroundHandler) {
-        if (predictiveBackSystemAnimations()) {
+        if (predictiveBackSystemAnims()) {
             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation aconfig flag is enabled, therefore "
                     + "developer settings flag is ignored and no content observer registered");
             return;
@@ -267,7 +267,7 @@
      */
     @ShellBackgroundThread
     private void updateEnableAnimationFromFlags() {
-        boolean isEnabled = predictiveBackSystemAnimations() || isDeveloperSettingEnabled();
+        boolean isEnabled = predictiveBackSystemAnims() || isDeveloperSettingEnabled();
         mEnableAnimations.set(isEnabled);
         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index e487328..bb0dd95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -769,8 +769,10 @@
         boolean swapped = false;
         for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) {
             View view = bubbleViews.get(newIndex);
-            final int oldIndex = mLayout.indexOfChild(view);
-            swapped |= animateSwap(view, oldIndex, newIndex, updateAllIcons, after);
+            if (view != null) {
+                final int oldIndex = mLayout.indexOfChild(view);
+                swapped |= animateSwap(view, oldIndex, newIndex, updateAllIcons, after);
+            }
         }
         if (!swapped) {
             // All bubbles were at the right position. Make sure badges and z order is correct.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 1211451..bd8ce80 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
+import android.view.Gravity;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewTreeObserver;
@@ -74,10 +75,6 @@
     private DismissView mDismissView;
     private @Nullable Consumer<String> mUnBubbleConversationCallback;
 
-    // TODO(b/273310265) - currently the view is always on the right, need to update for RTL.
-    /** Whether the expanded view is displaying on the left of the screen or not. */
-    private boolean mOnLeft = false;
-
     /** Whether a bubble is expanded. */
     private boolean mIsExpanded = false;
 
@@ -154,10 +151,10 @@
         return mIsExpanded;
     }
 
-    // (TODO: b/273310265): BubblePositioner should be source of truth when this work is done.
+    // TODO(b/313661121) - when dragging is implemented, check user setting first
     /** Whether the expanded view is positioned on the left or right side of the screen. */
     public boolean isOnLeft() {
-        return mOnLeft;
+        return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
     }
 
     /** Shows the expanded view of the provided bubble. */
@@ -216,7 +213,7 @@
                         return Unit.INSTANCE;
                     });
 
-            addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
+            addView(mExpandedView, new LayoutParams(width, height, Gravity.LEFT));
         }
 
         if (mEducationViewController.isEducationVisible()) {
@@ -311,7 +308,7 @@
         lp.width = width;
         lp.height = height;
         mExpandedView.setLayoutParams(lp);
-        if (mOnLeft) {
+        if (isOnLeft()) {
             mExpandedView.setX(mPositioner.getInsets().left + padding);
         } else {
             mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 1a793a1..b2eeea7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -271,6 +271,9 @@
                     if (e.findPointerIndex(mDragPointerId) == -1) {
                         mDragPointerId = e.getPointerId(0);
                     }
+                    final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+                    // If a decor's resize drag zone is active, don't also try to reposition it.
+                    if (decoration.isHandlingDragResize()) break;
                     final int dragPointerIdx = e.findPointerIndex(mDragPointerId);
                     mDragPositioningCallback.onDragPositioningMove(
                             e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 1debb02..5a74255 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -286,6 +286,10 @@
         closeBackground.setTintList(buttonTintColor);
     }
 
+    boolean isHandlingDragResize() {
+        return mDragResizeListener.isHandlingDragResize();
+    }
+
     private void closeDragResizeListener() {
         if (mDragResizeListener == null) {
             return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index aabc1cf..554b1fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -491,8 +491,11 @@
                     return true;
                 }
                 case MotionEvent.ACTION_MOVE: {
+                    mShouldClick = false;
                     final DesktopModeWindowDecoration decoration =
                             mWindowDecorByTaskId.get(mTaskId);
+                    // If a decor's resize drag zone is active, don't also try to reposition it.
+                    if (decoration.isHandlingDragResize()) break;
                     decoration.closeMaximizeMenu();
                     if (e.findPointerIndex(mDragPointerId) == -1) {
                         mDragPointerId = e.getPointerId(0);
@@ -505,7 +508,6 @@
                             e.getRawX(dragPointerIdx),
                             newTaskBounds));
                     mIsDragging = true;
-                    mShouldClick = false;
                     return true;
                 }
                 case MotionEvent.ACTION_UP:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 53f806c..2023333 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -387,6 +387,10 @@
         return mHandleMenu != null;
     }
 
+    boolean isHandlingDragResize() {
+        return mDragResizeListener.isHandlingDragResize();
+    }
+
     private void loadAppInfo() {
         String packageName = mTaskInfo.realActivity.getPackageName();
         PackageManager pm = mContext.getApplicationContext().getPackageManager();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
index 8511a21..8b38f99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java
@@ -320,6 +320,10 @@
         }
     }
 
+    boolean isHandlingDragResize() {
+        return mInputEventReceiver.isHandlingEvents();
+    }
+
     @Override
     public void close() {
         mInputEventReceiver.dispose();
@@ -386,6 +390,10 @@
             finishInputEvent(inputEvent, handleInputEvent(inputEvent));
         }
 
+        boolean isHandlingEvents() {
+            return mShouldHandleEvents;
+        }
+
         private boolean handleInputEvent(InputEvent inputEvent) {
             if (!(inputEvent instanceof MotionEvent)) {
                 return false;
@@ -409,7 +417,6 @@
                         mShouldHandleEvents = isInResizeHandleBounds(x, y);
                     }
                     if (mShouldHandleEvents) {
-                        mInputManager.pilferPointers(mInputChannel.getToken());
                         mDragPointerId = e.getPointerId(0);
                         float rawX = e.getRawX(0);
                         float rawY = e.getRawY(0);
@@ -427,6 +434,7 @@
                     if (!mShouldHandleEvents) {
                         break;
                     }
+                    mInputManager.pilferPointers(mInputChannel.getToken());
                     int dragPointerIndex = e.findPointerIndex(mDragPointerId);
                     float rawX = e.getRawX(dragPointerIndex);
                     float rawY = e.getRawY(dragPointerIndex);
@@ -437,6 +445,7 @@
                 }
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
+                    mInputManager.pilferPointers(mInputChannel.getToken());
                     if (mShouldHandleEvents) {
                         int dragPointerIndex = e.findPointerIndex(mDragPointerId);
                         final Rect taskBounds = mCallback.onDragPositioningEnd(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index c1b18f9..7c6e69e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -61,6 +61,7 @@
     private int mCtrlType;
     private boolean mIsResizingOrAnimatingResize;
     @Surface.Rotation private int mRotation;
+    private boolean mVeilIsVisible;
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration,
@@ -94,7 +95,6 @@
                 mDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
         mRepositionStartPoint.set(x, y);
         if (isResizing()) {
-            mDesktopWindowDecoration.showResizeVeil(mTaskBoundsAtDragStart);
             if (!mDesktopWindowDecoration.mTaskInfo.isFocused) {
                 WindowContainerTransaction wct = new WindowContainerTransaction();
                 wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true);
@@ -119,8 +119,13 @@
         if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
                 mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
                 mDisplayController, mDesktopWindowDecoration)) {
-            mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
             mIsResizingOrAnimatingResize = true;
+            if (!mVeilIsVisible) {
+                mDesktopWindowDecoration.showResizeVeil(mRepositionTaskBounds);
+                mVeilIsVisible = true;
+            } else {
+                mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
+            }
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
             final SurfaceControl.Transaction t = mTransactionSupplier.get();
             DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
@@ -143,7 +148,7 @@
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
                 mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
-            } else {
+            } else if (mVeilIsVisible) {
                 // If bounds haven't changed, perform necessary veil reset here as startAnimation
                 // won't be called.
                 mDesktopWindowDecoration.hideResizeVeil();
@@ -163,6 +168,7 @@
         mCtrlType = CTRL_TYPE_UNDEFINED;
         mTaskBoundsAtDragStart.setEmpty();
         mRepositionStartPoint.set(0, 0);
+        mVeilIsVisible = false;
         return new Rect(mRepositionTaskBounds);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
index 0841210..86253f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositionerTest.kt
@@ -144,13 +144,13 @@
     }
 
     @Test
-    fun testDragResize_noMove_showsResizeVeil() {
+    fun testDragResize_noMove_doesNotShowResizeVeil() {
         taskPositioner.onDragPositioningStart(
             CTRL_TYPE_TOP or CTRL_TYPE_RIGHT,
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
-        verify(mockDesktopWindowDecoration).showResizeVeil(STARTING_BOUNDS)
+        verify(mockDesktopWindowDecoration, never()).showResizeVeil(STARTING_BOUNDS)
 
         taskPositioner.onDragPositioningEnd(
             STARTING_BOUNDS.left.toFloat(),
@@ -162,7 +162,7 @@
                         (change.windowSetMask and WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0 &&
                         change.configuration.windowConfiguration.bounds == STARTING_BOUNDS}},
             eq(taskPositioner))
-        verify(mockDesktopWindowDecoration).hideResizeVeil()
+        verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
     }
 
     @Test
@@ -212,7 +212,6 @@
             STARTING_BOUNDS.right.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
-        verify(mockDesktopWindowDecoration).showResizeVeil(STARTING_BOUNDS)
 
         taskPositioner.onDragPositioningMove(
             STARTING_BOUNDS.right.toFloat() + 10,
@@ -222,6 +221,7 @@
         val rectAfterMove = Rect(STARTING_BOUNDS)
         rectAfterMove.right += 10
         rectAfterMove.top += 10
+        verify(mockDesktopWindowDecoration).showResizeVeil(rectAfterMove)
         verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
@@ -237,7 +237,7 @@
         val rectAfterEnd = Rect(rectAfterMove)
         rectAfterEnd.right += 10
         rectAfterEnd.top += 10
-        verify(mockDesktopWindowDecoration, times(2)).updateResizeVeil(any())
+        verify(mockDesktopWindowDecoration).updateResizeVeil(any())
         verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
                 token == taskBinder &&
@@ -253,7 +253,6 @@
             STARTING_BOUNDS.left.toFloat(),
             STARTING_BOUNDS.top.toFloat()
         )
-        verify(mockDesktopWindowDecoration).showResizeVeil(STARTING_BOUNDS)
 
         taskPositioner.onDragPositioningMove(
             STARTING_BOUNDS.left.toFloat(),
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index eebf8aa..b40b73c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -716,7 +716,6 @@
     ],
     shared_libs: [
         "libmemunreachable",
-        "server_configurable_flags",
     ],
     srcs: [
         "tests/unit/main.cpp",
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index d4af087..a5a841e 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -23,6 +23,7 @@
 
 #include <mutex>
 
+#include "Properties.h"
 #include "utils/Macros.h"
 
 namespace android {
@@ -60,7 +61,13 @@
     static void setWideColorDataspace(ADataSpace dataspace);
 
     static void setSupportFp16ForHdr(bool supportFp16ForHdr);
-    static bool isSupportFp16ForHdr() { return get()->mSupportFp16ForHdr; };
+    static bool isSupportFp16ForHdr() {
+        if (!Properties::hdr10bitPlus) {
+            return false;
+        }
+
+        return get()->mSupportFp16ForHdr;
+    };
 
     static void setSupportMixedColorSpaces(bool supportMixedColorSpaces);
     static bool isSupportMixedColorSpaces() { return get()->mSupportMixedColorSpaces; };
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d58c872..755332ff 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -38,6 +38,9 @@
 constexpr bool clip_surfaceviews() {
     return false;
 }
+constexpr bool hdr_10bit_plus() {
+    return false;
+}
 }  // namespace hwui_flags
 #endif
 
@@ -105,6 +108,7 @@
 float Properties::maxHdrHeadroomOn8bit = 5.f;  // TODO: Refine this number
 
 bool Properties::clipSurfaceViews = false;
+bool Properties::hdr10bitPlus = false;
 
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
@@ -177,6 +181,7 @@
 
     clipSurfaceViews =
             base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
+    hdr10bitPlus = hwui_flags::hdr_10bit_plus();
 
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index b956fac..ec53070 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -336,6 +336,7 @@
     static float maxHdrHeadroomOn8bit;
 
     static bool clipSurfaceViews;
+    static bool hdr10bitPlus;
 
     static StretchEffectBehavior getStretchEffectBehavior() {
         return stretchEffectBehavior;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index e0f1f6e..326b6ed 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -650,9 +650,14 @@
             mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
             break;
         case ColorMode::Hdr:
-            mSurfaceColorType = SkColorType::kN32_SkColorType;
-            mSurfaceColorSpace = SkColorSpace::MakeRGB(
-                    GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
+            if (DeviceInfo::get()->isSupportFp16ForHdr()) {
+                mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+                mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+            } else {
+                mSurfaceColorType = SkColorType::kN32_SkColorType;
+                mSurfaceColorSpace = SkColorSpace::MakeRGB(
+                        GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
+            }
             break;
         case ColorMode::Hdr10:
             mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
@@ -669,8 +674,13 @@
 void SkiaPipeline::setTargetSdrHdrRatio(float ratio) {
     if (mColorMode == ColorMode::Hdr || mColorMode == ColorMode::Hdr10) {
         mTargetSdrHdrRatio = ratio;
-        mSurfaceColorSpace = SkColorSpace::MakeRGB(GetExtendedTransferFunction(mTargetSdrHdrRatio),
-                                                   SkNamedGamut::kDisplayP3);
+
+        if (mColorMode == ColorMode::Hdr && DeviceInfo::get()->isSupportFp16ForHdr()) {
+            mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+        } else {
+            mSurfaceColorSpace = SkColorSpace::MakeRGB(
+                    GetExtendedTransferFunction(mTargetSdrHdrRatio), SkNamedGamut::kDisplayP3);
+        }
     } else {
         mTargetSdrHdrRatio = 1.f;
     }
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index eb4d494..ac2a936 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -124,24 +124,19 @@
     // flush and submit all work to the gpu and wait for it to finish
     mGrContext->flushAndSubmit(GrSyncCpu::kYes);
 
-    switch (mode) {
-        case TrimLevel::BACKGROUND:
-            mGrContext->freeGpuResources();
-            SkGraphics::PurgeAllCaches();
-            mRenderThread.destroyRenderingContext();
-            break;
-        case TrimLevel::UI_HIDDEN:
-            // Here we purge all the unlocked scratch resources and then toggle the resources cache
-            // limits between the background and max amounts. This causes the unlocked resources
-            // that have persistent data to be purged in LRU order.
-            mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
-            SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
-            mGrContext->purgeUnlockedResources(toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
-            mGrContext->setResourceCacheLimit(mMaxResourceBytes);
-            SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
-            break;
-        default:
-            break;
+    if (mode >= TrimLevel::BACKGROUND) {
+        mGrContext->freeGpuResources();
+        SkGraphics::PurgeAllCaches();
+        mRenderThread.destroyRenderingContext();
+    } else if (mode == TrimLevel::UI_HIDDEN) {
+        // Here we purge all the unlocked scratch resources and then toggle the resources cache
+        // limits between the background and max amounts. This causes the unlocked resources
+        // that have persistent data to be purged in LRU order.
+        mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
+        SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
+        mGrContext->purgeUnlockedResources(toSkiaEnum(mMemoryPolicy.purgeScratchOnly));
+        mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+        SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
     }
 }
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index facf30b..2904dfe 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -441,22 +441,32 @@
             colorMode = ColorMode::Default;
         }
 
-        if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+        // TODO: maybe we want to get rid of the WCG check if overlay properties just works?
+        const bool canUseFp16 = DeviceInfo::get()->isSupportFp16ForHdr() ||
+                                DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType;
+
+        if (canUseFp16) {
             if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
                 colorMode = ColorMode::Default;
             } else {
                 config = mEglConfigF16;
             }
         }
+
         if (EglExtensions.glColorSpace) {
             attribs[0] = EGL_GL_COLORSPACE_KHR;
             switch (colorMode) {
                 case ColorMode::Default:
                     attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
                     break;
+                case ColorMode::Hdr:
+                    if (canUseFp16) {
+                        attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+                        break;
+                        // No fp16 support so fallthrough to HDR10
+                    }
                 // We don't have an EGL colorspace for extended range P3 that's used for HDR
                 // So override it after configuring the EGL context
-                case ColorMode::Hdr:
                 case ColorMode::Hdr10:
                     overrideWindowDataSpaceForHdr = true;
                     attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b002bbf..ea136ed 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -1079,7 +1079,7 @@
      * @return one of the values that can be set in {@link Builder#setEncoding(int)} or
      * {@link AudioFormat#ENCODING_INVALID} if not set.
      */
-    public int getEncoding() {
+    public @Encoding int getEncoding() {
         return mEncoding;
     }
 
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 46db777..587e35b 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -16,6 +16,9 @@
 
 package android.media;
 
+import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE;
+
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1635,6 +1638,34 @@
      */
     public static final String KEY_ALLOW_FRAME_DROP = "allow-frame-drop";
 
+    /**
+     * A key describing the desired codec importance for the application.
+     * <p>
+     * The associated value is a positive integer including zero.
+     * Higher value means lesser importance.
+     * <p>
+     * The resource manager may use the codec importance, along with other factors
+     * when reclaiming codecs from an application.
+     * The specifics of reclaim policy is device dependent, but specifying the codec importance,
+     * will allow the resource manager to prioritize reclaiming less important codecs
+     * (assigned higher values) from the (reclaim) requesting application first.
+     * So, the codec importance is only relevant within the context of that application.
+     * <p>
+     * The codec importance can be set:
+     * <ul>
+     * <li>through {@link MediaCodec#configure}. </li>
+     * <li>through {@link MediaCodec#setParameters} if the codec has been configured already,
+     * which allows the users to change the codec importance multiple times.
+     * </ul>
+     * Any change/update in codec importance is guaranteed upon the completion of the function call
+     * that sets the codec importance. So, in case of concurrent codec operations,
+     * make sure to wait for the change in codec importance, before using another codec.
+     * Note that unless specified, by default the codecs will have highest importance (of value 0).
+     *
+     */
+    @FlaggedApi(FLAG_CODEC_IMPORTANCE)
+    public static final String KEY_IMPORTANCE = "importance";
+
     /* package private */ MediaFormat(@NonNull Map<String, Object> map) {
         mMap = map;
     }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index db01950..7f8f1a3 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -29,6 +30,7 @@
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
+import android.media.tv.flags.Flags;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -2540,9 +2542,9 @@
          * <p>This is used to indicate the broadcast visibility type defined in the underlying
          * broadcast standard or country/operator profile, if applicable. For example,
          * {@code visible_service_flag} and {@code numeric_selection_flag} of
-         * {@code service_attribute_descriptor} in D-Book, {@code visible_service_flag} and
-         * {@code selectable_service_flag} of {@code ciplus_service_descriptor} in CI Plus 1.3
-         * specification.
+         * {@code service_attribute_descriptor} in D-Book, the specification for UK-based TV
+         * products, {@code visible_service_flag} and {@code selectable_service_flag} of
+         * {@code ciplus_service_descriptor} in the CI Plus 1.3 specification.
          *
          * <p>The value should match one of the following:
          * {@link #BROADCAST_VISIBILITY_TYPE_VISIBLE},
@@ -2553,8 +2555,8 @@
          * by default.
          *
          * <p>Type: INTEGER
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_BROADCAST_VISIBILITY_TYPES)
         public static final String COLUMN_BROADCAST_VISIBILITY_TYPE = "broadcast_visibility_type";
 
         /** @hide */
@@ -2571,8 +2573,8 @@
          * visible from users and selectable by users via normal service navigation mechanisms.
          *
          * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_BROADCAST_VISIBILITY_TYPES)
         public static final int BROADCAST_VISIBILITY_TYPE_VISIBLE = 0;
 
         /**
@@ -2581,18 +2583,18 @@
          * the logical channel number.
          *
          * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_BROADCAST_VISIBILITY_TYPES)
         public static final int BROADCAST_VISIBILITY_TYPE_NUMERIC_SELECTABLE_ONLY = 1;
 
         /**
          * The broadcast visibility type for invisible services. Use this type when the service
-         * is invisible from users and unselectable by users via any of normal service navigation
-         * mechanisms.
+         * is invisible from users and not able to be selected by users via any of the normal
+         * service navigation mechanisms.
          *
          * @see #COLUMN_BROADCAST_VISIBILITY_TYPE
-         * @hide
          */
+        @FlaggedApi(Flags.FLAG_BROADCAST_VISIBILITY_TYPES)
         public static final int BROADCAST_VISIBILITY_TYPE_INVISIBLE = 2;
 
         private Channels() {}
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 87da299..74bec3e 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -69,6 +69,7 @@
     jarjar_rules: ":nfc-jarjar-rules",
     lint: {
         strict_updatability_linting: true,
+        baseline_filename: "lint-baseline.xml",
     },
     apex_available: [
         "//apex_available:platform",
diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml
new file mode 100644
index 0000000..1dfdd29
--- /dev/null
+++ b/nfc/lint-baseline.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `new android.nfc.cardemulation.AidGroup`"
+        errorLine1="        AidGroup aidGroup = new AidGroup(aids, category);"
+        errorLine2="                            ~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="377"
+            column="29"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
+        errorLine1="            return (group != null ? group.getAids() : null);"
+        errorLine2="                                          ~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="537"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.AidGroup#getAids`"
+        errorLine1="                return (group != null ? group.getAids() : null);"
+        errorLine2="                                              ~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="547"
+            column="47"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1="            return (serviceInfo != null ? serviceInfo.getAids() : null);"
+        errorLine2="                                                      ~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="714"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getAids`"
+        errorLine1="                return (serviceInfo != null ? serviceInfo.getAids() : null);"
+        errorLine2="                                                          ~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="724"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                if (!serviceInfo.isOnHost()) {"
+        errorLine2="                                 ~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="755"
+            column="34"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                    return serviceInfo.getOffHostSecureElement() == null ?"
+        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="756"
+            column="40"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1='                            "OffHost" : serviceInfo.getOffHostSecureElement();'
+        errorLine2="                                                    ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="757"
+            column="53"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#isOnHost`"
+        errorLine1="                    if (!serviceInfo.isOnHost()) {"
+        errorLine2="                                     ~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="772"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1="                        return serviceInfo.getOffHostSecureElement() == null ?"
+        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="773"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getOffHostSecureElement`"
+        errorLine1='                                "Offhost" : serviceInfo.getOffHostSecureElement();'
+        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="774"
+            column="57"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
+        errorLine1="            return (serviceInfo != null ? serviceInfo.getDescription() : null);"
+        errorLine2="                                                      ~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="798"
+            column="55"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.nfc.cardemulation.ApduServiceInfo#getDescription`"
+        errorLine1="                return (serviceInfo != null ? serviceInfo.getDescription() : null);"
+        errorLine2="                                                          ~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="808"
+            column="59"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="        if (!activity.isResumed()) {"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="1032"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="        if (!activity.isResumed()) {"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/CardEmulation.java"
+            line="1066"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="            resumed = activity.isResumed();"
+        errorLine2="                               ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/NfcActivityManager.java"
+            line="124"
+            column="32"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="        if (!activity.isResumed()) {"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+            line="2457"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="        if (!activity.isResumed()) {"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
+            line="315"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 35 (current min is 34): `android.app.Activity#isResumed`"
+        errorLine1="        if (!activity.isResumed()) {"
+        errorLine2="                      ~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/NfcFCardEmulation.java"
+            line="351"
+            column="23"/>
+    </issue>
+
+</issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 5da4b95..c2cb757 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -9,6 +9,9 @@
 
 android_library {
     name: "SettingsLib",
+    defaults: [
+        "SettingsLintDefaults",
+    ],
 
     static_libs: [
         "androidx.localbroadcastmanager_localbroadcastmanager",
@@ -60,8 +63,15 @@
         "src/**/*.java",
         "src/**/*.kt",
     ],
+}
+
+// defaults for lint option
+java_defaults {
+    name: "SettingsLintDefaults",
     lint: {
-        extra_check_modules: ["SettingsLibLintChecker"],
+        extra_check_modules: [
+            "SettingsLibLintChecker",
+        ],
     },
 }
 
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 1c830c1..74b556e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -162,6 +162,7 @@
                 uid = checkNotNull(applicationInfo).uid,
                 packageName = packageName) })
         RestrictedSwitchPreference(switchModel, restrictions, restrictionsProviderFactory)
+        InfoPageAdditionalContent(record, isAllowed)
     }
 }
 
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 916d83a..3f7a852 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -77,6 +77,9 @@
      * Sets whether the permission is allowed for the given app.
      */
     fun setAllowed(record: T, newAllowed: Boolean)
+
+    @Composable
+    fun InfoPageAdditionalContent(record: T, isAllowed: () -> Boolean?){}
 }
 
 interface TogglePermissionAppListProvider {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 071afaf..f7f0673 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -117,6 +117,12 @@
             }
         }
 
+        if (cachedDevice.isHearingAidDevice()) {
+            return new Pair<>(getBluetoothDrawable(context,
+                    com.android.internal.R.drawable.ic_bt_hearing_aid),
+                    context.getString(R.string.bluetooth_talkback_hearing_aids));
+        }
+
         List<LocalBluetoothProfile> profiles = cachedDevice.getProfiles();
         int resId = 0;
         for (LocalBluetoothProfile profile : profiles) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 032a838..560bc46 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -412,6 +412,7 @@
 
     public void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
         mHearingAidInfo = hearingAidInfo;
+        dispatchAttributesChanged();
     }
 
     public HearingAidInfo getHearingAidInfo() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 7409eea..f7ec80b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -16,6 +16,7 @@
 package com.android.settingslib.bluetooth;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -87,6 +88,14 @@
     }
 
     @Test
+    public void getBtClassDrawableWithDescription_typeHearingAid_returnHearingAidDrawable() {
+        when(mCachedBluetoothDevice.isHearingAidDevice()).thenReturn(true);
+        BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedBluetoothDevice);
+
+        verify(mContext).getDrawable(com.android.internal.R.drawable.ic_bt_hearing_aid);
+    }
+
+    @Test
     public void getBtRainbowDrawableWithDescription_normalHeadset_returnAdaptiveIcon() {
         when(mBluetoothDevice.getMetadata(
                 BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("false".getBytes());
diff --git a/packages/SettingsProvider/TEST_MAPPING b/packages/SettingsProvider/TEST_MAPPING
index 890510f..0eed2b7 100644
--- a/packages/SettingsProvider/TEST_MAPPING
+++ b/packages/SettingsProvider/TEST_MAPPING
@@ -11,5 +11,10 @@
                 }
             ]
         }
+    ],
+    "postsubmit": [
+        {
+            "name": "CtsDeviceConfigTestCases"
+        }
     ]
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index ecac5ee..edbc0b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -14,3 +14,11 @@
     bug: "311155098"
     is_fixed_read_only: true
 }
+
+flag {
+  name: "configurable_font_scale_default"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Whether the font_scale is read from a device dependent configuration file"
+  bug: "319808237"
+  is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 507d9c4..3dfc454 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -559,6 +559,9 @@
     <!-- Permission required for CTS test - android.server.biometrics -->
     <uses-permission android:name="android.permission.TEST_BIOMETRIC" />
 
+    <!-- Permission required for CTS test - android.server.biometrics -->
+    <uses-permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
+
     <!-- Permissions required for CTS test - NotificationManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
 
@@ -891,6 +894,9 @@
     <!-- Permission required for Cts test - CtsNotificationTestCases -->
     <uses-permission android:name="android.permission.RECEIVE_SENSITIVE_NOTIFICATIONS" />
 
+    <!-- Permission required for BinaryTransparencyService shell API and host test -->
+    <uses-permission android:name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7443e4c..168e6e0 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -355,6 +355,8 @@
 
     <uses-permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT" />
 
+    <uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" />
+
     <!-- Listen to (dis-)connection of external displays and enable / disable them. -->
     <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
 
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index f7b1a26..7ba889b 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -36,3 +36,10 @@
     description: "Animates the floating menu's transition between curved and jagged edges."
     bug: "281140482"
 }
+
+flag {
+    name: "create_windowless_window_magnifier"
+    namespace: "accessibility"
+    description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
+    bug: "280992417"
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c23a49c..b464498 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -343,3 +343,10 @@
    description: "Relocate Smartspace to bottom of the Lock Screen"
    bug: "316212788"
 }
+
+flag {
+   name: "pin_input_field_styled_focus_state"
+   namespace: "systemui"
+   description: "Enables styled focus states on pin input field if keyboard is connected"
+   bug: "316106516"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index c8e18d7..0a1a98f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -610,7 +610,7 @@
                 model.appWidgetHost.setInteractionHandler(viewModel.getInteractionHandler())
                 val view =
                     model.appWidgetHost
-                        .createView(context, model.appWidgetId, model.providerInfo)
+                        .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
                         .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
                 model.appWidgetHost.setInteractionHandler(null)
                 view
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 900616f..42fcd13 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -16,23 +16,55 @@
 
 package com.android.systemui.keyguard.ui.composable.section
 
+import android.content.Context
+import android.view.ViewGroup
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.statusbar.notification.stack.AmbientState
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationStackAppearanceViewBinder
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import javax.inject.Inject
 
 class NotificationSection
 @Inject
 constructor(
+    @Application context: Context,
     private val viewModel: NotificationsPlaceholderViewModel,
+    controller: NotificationStackScrollLayoutController,
+    sceneContainerFlags: SceneContainerFlags,
+    sharedNotificationContainer: SharedNotificationContainer,
+    stackScrollLayout: NotificationStackScrollLayout,
+    notificationStackAppearanceViewModel: NotificationStackAppearanceViewModel,
+    ambientState: AmbientState,
 ) {
+    init {
+        if (sceneContainerFlags.flexiNotifsEnabled()) {
+            (stackScrollLayout.parent as? ViewGroup)?.removeView(stackScrollLayout)
+            sharedNotificationContainer.addNotificationStackScrollLayout(stackScrollLayout)
+
+            NotificationStackAppearanceViewBinder.bind(
+                context,
+                sharedNotificationContainer,
+                notificationStackAppearanceViewModel,
+                ambientState,
+                controller,
+            )
+        }
+    }
+
     @Composable
     fun SceneScope.Notifications(modifier: Modifier = Modifier) {
         NotificationStack(
             viewModel = viewModel,
-            isScrimVisible = false,
             modifier = modifier,
         )
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 0b26ae9..e835d3e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -22,6 +22,7 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.MaterialTheme
@@ -43,8 +44,10 @@
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.height
 import com.android.systemui.notifications.ui.composable.Notifications.Form
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import kotlin.math.roundToInt
 
 object Notifications {
     object Elements {
@@ -77,32 +80,52 @@
     )
 }
 
-/** Adds the space where notification stack will appear in the scene. */
+/** Adds the space where notification stack should appear in the scene. */
 @Composable
 fun SceneScope.NotificationStack(
     viewModel: NotificationsPlaceholderViewModel,
-    isScrimVisible: Boolean,
+    modifier: Modifier = Modifier,
+) {
+    NotificationPlaceholder(
+        viewModel = viewModel,
+        form = Form.Stack,
+        modifier = modifier,
+    )
+}
+
+/**
+ * Adds the space where notification stack should appear in the scene, with a scrim and nested
+ * scrolling.
+ */
+@Composable
+fun SceneScope.NotificationScrollingStack(
+    viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
     val cornerRadius by viewModel.cornerRadiusDp.collectAsState()
 
-    Box(modifier = modifier) {
-        if (isScrimVisible) {
-            Box(
-                modifier =
-                    Modifier.element(Notifications.Elements.NotificationScrim)
-                        .fillMaxSize()
-                        .graphicsLayer {
-                            shape = RoundedCornerShape(cornerRadius.dp)
-                            clip = true
-                        }
-                        .background(MaterialTheme.colorScheme.surface)
-            )
-        }
+    val contentHeight by viewModel.intrinsicContentHeight.collectAsState()
+
+    val expansionFraction by viewModel.expandFraction.collectAsState(0f)
+
+    Box(
+        modifier =
+            modifier
+                .verticalNestedScrollToScene()
+                .fillMaxWidth()
+                .element(Notifications.Elements.NotificationScrim)
+                .graphicsLayer {
+                    shape = RoundedCornerShape(cornerRadius.dp)
+                    clip = true
+                    alpha = expansionFraction
+                }
+                .background(MaterialTheme.colorScheme.surface)
+                .debugBackground(viewModel, Color(0.5f, 0.5f, 0f, 0.2f))
+    ) {
         NotificationPlaceholder(
             viewModel = viewModel,
             form = Form.Stack,
-            modifier = Modifier.fillMaxSize(),
+            modifier = Modifier.fillMaxWidth().height { contentHeight.roundToInt() }
         )
     }
 }
@@ -159,6 +182,7 @@
                     debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
                 }
                 .onPlaced { coordinates: LayoutCoordinates ->
+                    viewModel.onContentTopChanged(coordinates.positionInWindow().y)
                     debugLog(viewModel) {
                         "STACK onPlaced:" +
                             " size=${coordinates.size}" +
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index bded98d..747faab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -25,6 +25,7 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
+import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.Edge
 import com.android.systemui.scene.shared.model.SceneKey
@@ -66,6 +67,7 @@
         modifier: Modifier,
     ) {
         Box(modifier = modifier) {
+            Box(modifier = Modifier.fillMaxSize().element(Notifications.Elements.NotificationScrim))
             HeadsUpNotificationSpace(
                 viewModel = notificationsViewModel,
                 modifier = Modifier.padding(16.dp).fillMaxSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
index 6bb525a..0c2c519 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToShadeTransition.kt
@@ -3,12 +3,12 @@
 import androidx.compose.animation.core.tween
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
-import com.android.systemui.notifications.ui.composable.Notifications
-import com.android.systemui.scene.ui.composable.Shade
+import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.shade.ui.composable.ShadeHeader
 
 fun TransitionBuilder.goneToShadeTransition() {
     spec = tween(durationMillis = 500)
 
-    translate(Shade.rootElementKey, Edge.Top, true)
-    fade(Notifications.Elements.NotificationScrim)
+    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContent) }
+    translate(QuickSettings.Elements.Content, Edge.Top, true)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 9c0f1fe..085dcf4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -22,11 +22,9 @@
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
@@ -47,7 +45,7 @@
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
-import com.android.systemui.notifications.ui.composable.NotificationStack
+import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Direction
@@ -148,35 +146,27 @@
     mediaHost: MediaHost,
     modifier: Modifier = Modifier,
 ) {
+    val localDensity = LocalDensity.current
     val layoutWidth = remember { mutableStateOf(0) }
 
-    Box(modifier.element(Shade.Elements.Scrim)) {
-        Spacer(
-            modifier =
-                Modifier.element(Shade.Elements.ScrimBackground)
-                    .fillMaxSize()
-                    .background(MaterialTheme.colorScheme.scrim, shape = Shade.Shapes.Scrim)
-        )
+    Box(
+        modifier =
+            modifier.element(Shade.Elements.Scrim).background(MaterialTheme.colorScheme.scrim),
+    )
+    Box {
         Column(
             horizontalAlignment = Alignment.CenterHorizontally,
-            modifier =
-                Modifier.fillMaxSize()
-                    .clickable(onClick = { viewModel.onContentClicked() })
-                    .padding(
-                        start = Shade.Dimensions.HorizontalPadding,
-                        end = Shade.Dimensions.HorizontalPadding,
-                        bottom = 48.dp
-                    )
+            modifier = Modifier.fillMaxWidth().clickable(onClick = { viewModel.onContentClicked() })
         ) {
             CollapsedShadeHeader(
                 viewModel = viewModel.shadeHeaderViewModel,
                 createTintedIconManager = createTintedIconManager,
                 createBatteryMeterViewController = createBatteryMeterViewController,
                 statusBarIconController = statusBarIconController,
+                modifier = Modifier.padding(horizontal = Shade.Dimensions.HorizontalPadding)
             )
-            Spacer(modifier = Modifier.height(16.dp))
             QuickSettings(
-                modifier = Modifier.wrapContentHeight(),
+                modifier = Modifier.height(130.dp),
                 viewModel.qsSceneAdapter,
             )
 
@@ -202,16 +192,15 @@
                         },
                     mediaHost = mediaHost,
                     layoutWidth = layoutWidth.value,
-                    layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(),
+                    layoutHeight = with(localDensity) { mediaHeight.toPx() }.toInt(),
                     carouselController = mediaCarouselController,
                 )
             }
 
             Spacer(modifier = Modifier.height(16.dp))
-            NotificationStack(
+            NotificationScrollingStack(
                 viewModel = viewModel.notifications,
-                isScrimVisible = true,
-                modifier = Modifier.weight(1f),
+                modifier = Modifier.fillMaxWidth().weight(1f),
             )
         }
     }
diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
index 8fe9656..fc337fb 100644
--- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml
@@ -30,11 +30,6 @@
             android:enabled="false"
             tools:replace="android:authorities"
             tools:node="remove" />
-        <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
-            android:authorities="com.android.systemui.test.keyguard.disabled"
-            android:enabled="false"
-            tools:replace="android:authorities"
-            tools:node="remove" />
         <provider android:name="com.android.systemui.keyguard.CustomizationProvider"
             android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
             android:enabled="false"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index e893169..c4bcb53 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -107,7 +108,7 @@
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
                 mEmergencyButtonController, mFalsingCollector, featureFlags,
-                mSelectedUserInteractor) {
+                mSelectedUserInteractor, new FakeKeyboardRepository()) {
             @Override
             public void onResume(int reason) {
                 super.onResume(reason);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 78b854e..c2efc05 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
@@ -141,7 +142,8 @@
             postureController,
             featureFlags,
             mSelectedUserInteractor,
-            uiEventLogger
+            uiEventLogger,
+            FakeKeyboardRepository()
         )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index bc3ca1b..2a793ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -47,16 +47,20 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.SessionTracker
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
@@ -66,6 +70,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.statusbar.policy.UserSwitcherController
+import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.util.mockito.any
@@ -156,7 +161,7 @@
     private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
     private lateinit var keyguardPasswordView: KeyguardPasswordView
     private lateinit var testableResources: TestableResources
-    private lateinit var sceneTestUtils: SceneTestUtils
+    private lateinit var kosmos: Kosmos
     private lateinit var sceneInteractor: SceneInteractor
     private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
     private lateinit var deviceEntryInteractor: DeviceEntryInteractor
@@ -222,15 +227,15 @@
                 mSelectedUserInteractor,
             )
 
-        sceneTestUtils = SceneTestUtils(this)
-        sceneInteractor = sceneTestUtils.sceneInteractor()
+        kosmos = testKosmos()
+        sceneInteractor = kosmos.sceneInteractor
         keyguardTransitionInteractor =
-            KeyguardTransitionInteractorFactory.create(sceneTestUtils.testScope.backgroundScope)
+            KeyguardTransitionInteractorFactory.create(kosmos.testScope.backgroundScope)
                 .keyguardTransitionInteractor
         sceneTransitionStateFlow =
             MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
         sceneInteractor.setTransitionState(sceneTransitionStateFlow)
-        deviceEntryInteractor = sceneTestUtils.deviceEntryInteractor()
+        deviceEntryInteractor = kosmos.deviceEntryInteractor
 
         mSetFlagsRule.disableFlags(FLAG_SIDEFPS_CONTROLLER_REFACTOR)
         underTest =
@@ -249,7 +254,7 @@
                 falsingManager,
                 userSwitcherController,
                 featureFlags,
-                sceneTestUtils.fakeSceneContainerFlags,
+                kosmos.fakeSceneContainerFlags,
                 globalSettings,
                 sessionTracker,
                 Optional.of(sideFpsController),
@@ -259,7 +264,7 @@
                 audioManager,
                 faceAuthInteractor,
                 mock(),
-                { JavaAdapter(sceneTestUtils.testScope.backgroundScope) },
+                { JavaAdapter(kosmos.testScope.backgroundScope) },
                 mSelectedUserInteractor,
                 deviceProvisionedController,
                 faceAuthAccessibilityDelegate,
@@ -786,8 +791,8 @@
 
     @Test
     fun dismissesKeyguard_whenSceneChangesToGone() =
-        sceneTestUtils.testScope.runTest {
-            sceneTestUtils.fakeSceneContainerFlags.enabled = true
+        kosmos.testScope.runTest {
+            kosmos.fakeSceneContainerFlags.enabled = true
             // Upon init, we have never dismisses the keyguard.
             underTest.onInit()
             runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index f775175..0959f1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.keyguard
 
-import android.telephony.PinResult
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper
 import android.view.LayoutInflater
@@ -28,9 +27,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
 import com.android.systemui.res.R
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -39,7 +40,6 @@
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -75,8 +75,7 @@
         `when`(messageAreaControllerFactory.create(Mockito.any(KeyguardMessageArea::class.java)))
             .thenReturn(keyguardMessageAreaController)
         `when`(telephonyManager.createForSubscriptionId(anyInt())).thenReturn(telephonyManager)
-        `when`(telephonyManager.supplyIccLockPin(anyString()))
-            .thenReturn(mock(PinResult::class.java))
+        `when`(telephonyManager.supplyIccLockPin(anyString())).thenReturn(mock())
         simPinView =
             LayoutInflater.from(context).inflate(R.layout.keyguard_sim_pin_view, null)
                 as KeyguardSimPinView
@@ -97,7 +96,8 @@
                 falsingCollector,
                 emergencyButtonController,
                 fakeFeatureFlags,
-                mSelectedUserInteractor
+                mSelectedUserInteractor,
+                FakeKeyboardRepository()
             )
         underTest.init()
         underTest.onViewAttached()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 45a60199..1281e44 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
 import com.android.systemui.res.R
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
@@ -91,6 +92,7 @@
                 emergencyButtonController,
                 fakeFeatureFlags,
                 mSelectedUserInteractor,
+                FakeKeyboardRepository()
             )
         underTest.init()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
index 27d1eb7..c86c747 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -17,14 +17,15 @@
 package com.android.systemui.accessibility.data.repository
 
 import android.os.UserHandle
+import android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -51,6 +52,7 @@
         underTest =
             ColorCorrectionRepositoryImpl(
                 testDispatcher,
+                scope.backgroundScope,
                 settings,
             )
     }
@@ -58,83 +60,78 @@
     @Test
     fun isEnabled_initiallyGetsSettingsValue() =
         scope.runTest {
+            val actualValue by collectLastValue(underTest.isEnabled(testUser1))
+
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                1,
+                SETTING_NAME,
+                ENABLED,
                 testUser1.identifier
             )
-
-            underTest =
-                ColorCorrectionRepositoryImpl(
-                    testDispatcher,
-                    settings,
-                )
-
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
             runCurrent()
 
-            val actualValue: Boolean = underTest.isEnabled(testUser1).first()
             Truth.assertThat(actualValue).isTrue()
         }
 
     @Test
     fun isEnabled_settingUpdated_valueUpdated() =
         scope.runTest {
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
+            val flowValues: List<Boolean> by collectValues(underTest.isEnabled(testUser1))
 
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.DISABLED,
+                SETTING_NAME,
+                DISABLED,
                 testUser1.identifier
             )
             runCurrent()
-            Truth.assertThat(underTest.isEnabled(testUser1).first()).isFalse()
 
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.ENABLED,
+                SETTING_NAME,
+                ENABLED,
                 testUser1.identifier
             )
             runCurrent()
-            Truth.assertThat(underTest.isEnabled(testUser1).first()).isTrue()
 
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.DISABLED,
+                SETTING_NAME,
+                DISABLED,
                 testUser1.identifier
             )
             runCurrent()
-            Truth.assertThat(underTest.isEnabled(testUser1).first()).isFalse()
+
+            Truth.assertThat(flowValues.size).isEqualTo(3)
+            Truth.assertThat(flowValues).containsExactly(false, true, false).inOrder()
         }
 
     @Test
     fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() =
         scope.runTest {
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
+            val lastValueUser1 by collectLastValue(underTest.isEnabled(testUser1))
+            val lastValueUser2 by collectLastValue(underTest.isEnabled(testUser2))
+
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.DISABLED,
+                SETTING_NAME,
+                DISABLED,
                 testUser1.identifier
             )
-            underTest.isEnabled(testUser2).launchIn(backgroundScope)
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.DISABLED,
+                SETTING_NAME,
+                DISABLED,
                 testUser2.identifier
             )
-
             runCurrent()
-            Truth.assertThat(underTest.isEnabled(testUser1).first()).isFalse()
-            Truth.assertThat(underTest.isEnabled(testUser2).first()).isFalse()
+
+            Truth.assertThat(lastValueUser1).isFalse()
+            Truth.assertThat(lastValueUser2).isFalse()
 
             settings.putIntForUser(
-                ColorCorrectionRepositoryImpl.SETTING_NAME,
-                ColorCorrectionRepositoryImpl.ENABLED,
+                SETTING_NAME,
+                ENABLED,
                 testUser1.identifier
             )
             runCurrent()
-            Truth.assertThat(underTest.isEnabled(testUser1).first()).isTrue()
-            Truth.assertThat(underTest.isEnabled(testUser2).first()).isFalse()
+
+            Truth.assertThat(lastValueUser1).isTrue()
+            Truth.assertThat(lastValueUser2).isFalse()
         }
 
     @Test
@@ -146,10 +143,10 @@
 
             val actualValue =
                 settings.getIntForUser(
-                    ColorCorrectionRepositoryImpl.SETTING_NAME,
+                    SETTING_NAME,
                     testUser1.identifier
                 )
-            Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.ENABLED)
+            Truth.assertThat(actualValue).isEqualTo(ENABLED)
         }
 
     @Test
@@ -161,9 +158,15 @@
 
             val actualValue =
                 settings.getIntForUser(
-                    ColorCorrectionRepositoryImpl.SETTING_NAME,
+                    SETTING_NAME,
                     testUser1.identifier
                 )
-            Truth.assertThat(actualValue).isEqualTo(ColorCorrectionRepositoryImpl.DISABLED)
+            Truth.assertThat(actualValue).isEqualTo(DISABLED)
         }
+
+    companion object {
+        private const val SETTING_NAME = ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
+        private const val DISABLED = 0
+        private const val ENABLED = 1
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
index 423e124..4853529 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
@@ -21,11 +21,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -52,6 +52,7 @@
         underTest =
             ColorInversionRepositoryImpl(
                 testDispatcher,
+                scope.backgroundScope,
                 settings,
             )
     }
@@ -59,55 +60,47 @@
     @Test
     fun isEnabled_initiallyGetsSettingsValue() =
         scope.runTest {
-            settings.putIntForUser(SETTING_NAME, 1, testUser1.identifier)
+            val actualValue by collectLastValue(underTest.isEnabled(testUser1))
 
-            underTest =
-                ColorInversionRepositoryImpl(
-                    testDispatcher,
-                    settings,
-                )
-
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
+            settings.putIntForUser(SETTING_NAME, ENABLED, testUser1.identifier)
             runCurrent()
 
-            val actualValue: Boolean = underTest.isEnabled(testUser1).first()
             assertThat(actualValue).isTrue()
         }
 
     @Test
     fun isEnabled_settingUpdated_valueUpdated() =
         scope.runTest {
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
+            val flowValues: List<Boolean> by
+                collectValues(underTest.isEnabled(testUser1))
 
             settings.putIntForUser(SETTING_NAME, DISABLED, testUser1.identifier)
             runCurrent()
-            assertThat(underTest.isEnabled(testUser1).first()).isFalse()
-
             settings.putIntForUser(SETTING_NAME, ENABLED, testUser1.identifier)
             runCurrent()
-            assertThat(underTest.isEnabled(testUser1).first()).isTrue()
-
             settings.putIntForUser(SETTING_NAME, DISABLED, testUser1.identifier)
             runCurrent()
-            assertThat(underTest.isEnabled(testUser1).first()).isFalse()
+
+            assertThat(flowValues.size).isEqualTo(3)
+            assertThat(flowValues).containsExactly(false, true, false).inOrder()
         }
 
     @Test
     fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() =
         scope.runTest {
-            underTest.isEnabled(testUser1).launchIn(backgroundScope)
-            settings.putIntForUser(SETTING_NAME, DISABLED, testUser1.identifier)
-            underTest.isEnabled(testUser2).launchIn(backgroundScope)
-            settings.putIntForUser(SETTING_NAME, DISABLED, testUser2.identifier)
+            val lastValueUser1 by collectLastValue(underTest.isEnabled(testUser1))
+            val lastValueUser2 by collectLastValue(underTest.isEnabled(testUser2))
 
+            settings.putIntForUser(SETTING_NAME, DISABLED, testUser1.identifier)
+            settings.putIntForUser(SETTING_NAME, DISABLED, testUser2.identifier)
             runCurrent()
-            assertThat(underTest.isEnabled(testUser1).first()).isFalse()
-            assertThat(underTest.isEnabled(testUser2).first()).isFalse()
+            assertThat(lastValueUser1).isFalse()
+            assertThat(lastValueUser2).isFalse()
 
             settings.putIntForUser(SETTING_NAME, ENABLED, testUser1.identifier)
             runCurrent()
-            assertThat(underTest.isEnabled(testUser1).first()).isTrue()
-            assertThat(underTest.isEnabled(testUser2).first()).isFalse()
+            assertThat(lastValueUser1).isTrue()
+            assertThat(lastValueUser2).isFalse()
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index b4d4e1f..caf9219 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -29,10 +29,13 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.sceneContainerFlags
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
@@ -59,8 +62,8 @@
     @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
-    private val testUtils = SceneTestUtils(this)
-    private val testScope = testUtils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
     private val clock = FakeSystemClock()
     private val userRepository = FakeUserRepository()
     private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
@@ -82,8 +85,8 @@
         underTest =
             AuthenticationRepositoryImpl(
                 applicationScope = testScope.backgroundScope,
-                backgroundDispatcher = testUtils.testDispatcher,
-                flags = testUtils.fakeSceneContainerFlags,
+                backgroundDispatcher = kosmos.testDispatcher,
+                flags = kosmos.sceneContainerFlags,
                 clock = clock,
                 getSecurityMode = getSecurityMode,
                 userRepository = userRepository,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 10c16bd..cb8cebf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -22,6 +22,7 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
@@ -29,7 +30,8 @@
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,9 +47,9 @@
 @RunWith(AndroidJUnit4::class)
 class AuthenticationInteractorTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val underTest = utils.authenticationInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val underTest = kosmos.authenticationInteractor
 
     private val onAuthenticationResult by
         testScope.collectLastValue(underTest.onAuthenticationResult)
@@ -62,7 +64,7 @@
             assertThat(authMethod).isEqualTo(Pin)
             assertThat(underTest.getAuthenticationMethod()).isEqualTo(Pin)
 
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
 
             assertThat(authMethod).isEqualTo(Password)
             assertThat(underTest.getAuthenticationMethod()).isEqualTo(Password)
@@ -74,7 +76,7 @@
             val authMethod by collectLastValue(underTest.authenticationMethod)
             runCurrent()
 
-            utils.authenticationRepository.setAuthenticationMethod(None)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(None)
 
             assertThat(authMethod).isEqualTo(None)
             assertThat(underTest.getAuthenticationMethod()).isEqualTo(None)
@@ -83,7 +85,7 @@
     @Test
     fun authenticate_withCorrectPin_succeeds() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
 
             assertSucceeded(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
         }
@@ -91,7 +93,7 @@
     @Test
     fun authenticate_withIncorrectPin_fails() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
 
             assertFailed(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4)))
         }
@@ -99,7 +101,7 @@
     @Test(expected = IllegalArgumentException::class)
     fun authenticate_withEmptyPin_throwsException() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             underTest.authenticate(listOf())
         }
 
@@ -107,7 +109,7 @@
     fun authenticate_withCorrectMaxLengthPin_succeeds() =
         testScope.runTest {
             val correctMaxLengthPin = List(16) { 9 }
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 overrideCredential(correctMaxLengthPin)
             }
@@ -124,7 +126,7 @@
             // If the policy changes, there is work to do in SysUI.
             assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17)
 
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
 
             assertFailed(underTest.authenticate(List(17) { 9 }))
         }
@@ -132,7 +134,7 @@
     @Test
     fun authenticate_withCorrectPassword_succeeds() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
 
             assertSucceeded(underTest.authenticate("password".toList()))
         }
@@ -140,7 +142,7 @@
     @Test
     fun authenticate_withIncorrectPassword_fails() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
 
             assertFailed(underTest.authenticate("alohomora".toList()))
         }
@@ -148,7 +150,7 @@
     @Test
     fun authenticate_withCorrectPattern_succeeds() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pattern)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pattern)
 
             assertSucceeded(underTest.authenticate(FakeAuthenticationRepository.PATTERN))
         }
@@ -156,7 +158,7 @@
     @Test
     fun authenticate_withIncorrectPattern_fails() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pattern)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pattern)
             val wrongPattern =
                 listOf(
                     AuthenticationPatternCoordinate(x = 2, y = 0),
@@ -172,7 +174,7 @@
     fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNull() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
             }
@@ -182,14 +184,14 @@
 
             assertSkipped(underTest.authenticate(shorterPin, tryAutoConfirm = true))
             assertThat(underTest.lockoutEndTimestamp).isNull()
-            assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
+            assertThat(kosmos.fakeAuthenticationRepository.lockoutStartedReportCount).isEqualTo(0)
         }
 
     @Test
     fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalse() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
             }
@@ -207,7 +209,7 @@
     fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalse() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
             }
@@ -225,7 +227,7 @@
     fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrue() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
             }
@@ -241,7 +243,7 @@
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
                 reportLockoutStarted(42)
@@ -258,7 +260,7 @@
     @Test
     fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNull() =
         testScope.runTest {
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(false)
             }
@@ -271,7 +273,7 @@
     @Test
     fun tryAutoConfirm_withoutCorrectPassword_returnsNull() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
 
             assertSkipped(underTest.authenticate("password".toList(), tryAutoConfirm = true))
         }
@@ -280,7 +282,7 @@
     fun isAutoConfirmEnabled_featureDisabled_returnsFalse() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(false)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(false)
 
             assertThat(isAutoConfirmEnabled).isFalse()
         }
@@ -289,7 +291,7 @@
     fun isAutoConfirmEnabled_featureEnabled_returnsTrue() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             assertThat(isAutoConfirmEnabled).isTrue()
         }
@@ -298,7 +300,7 @@
     fun isAutoConfirmEnabled_featureEnabledButDisabledByLockout() =
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             // The feature is enabled.
             assertThat(isAutoConfirmEnabled).isTrue()
@@ -308,7 +310,7 @@
                 assertFailed(underTest.authenticate(listOf(5, 6, 7))) // Wrong PIN
             }
             assertThat(underTest.lockoutEndTimestamp).isNotNull()
-            assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
+            assertThat(kosmos.fakeAuthenticationRepository.lockoutStartedReportCount).isEqualTo(1)
 
             // Lockout disabled auto-confirm.
             assertThat(isAutoConfirmEnabled).isFalse()
@@ -336,7 +338,7 @@
             val failedAuthenticationAttempts by
                 collectLastValue(underTest.failedAuthenticationAttempts)
 
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
 
             assertSucceeded(underTest.authenticate(correctPin))
@@ -366,7 +368,7 @@
     @Test
     fun lockoutEndTimestamp() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
 
             underTest.authenticate(correctPin)
@@ -384,7 +386,7 @@
             val expectedLockoutEndTimestamp =
                 testScope.currentTime + FakeAuthenticationRepository.LOCKOUT_DURATION_MS
             assertThat(underTest.lockoutEndTimestamp).isEqualTo(expectedLockoutEndTimestamp)
-            assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(1)
+            assertThat(kosmos.fakeAuthenticationRepository.lockoutStartedReportCount).isEqualTo(1)
 
             // Correct PIN, but locked out, so doesn't attempt it:
             assertSkipped(underTest.authenticate(correctPin), assertNoResultEvents = false)
@@ -409,7 +411,7 @@
     fun upcomingWipe() =
         testScope.runTest {
             val upcomingWipe by collectLastValue(underTest.upcomingWipe)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             val correctPin = FakeAuthenticationRepository.DEFAULT_PIN
             val wrongPin = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
 
@@ -418,7 +420,7 @@
 
             var expectedFailedAttempts = 0
             var remainingFailedAttempts =
-                utils.authenticationRepository.getMaxFailedUnlockAttemptsForWipe()
+                kosmos.fakeAuthenticationRepository.getMaxFailedUnlockAttemptsForWipe()
             assertThat(remainingFailedAttempts)
                 .isGreaterThan(LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE)
 
@@ -458,7 +460,7 @@
     fun hintedPinLength_withoutAutoConfirm_isNull() =
         testScope.runTest {
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(false)
             }
@@ -470,11 +472,13 @@
     fun hintedPinLength_withAutoConfirmPinTooShort_isNull() =
         testScope.runTest {
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 overrideCredential(
                     buildList {
-                        repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) }
+                        repeat(kosmos.fakeAuthenticationRepository.hintedPinLength - 1) {
+                            add(it + 1)
+                        }
                     }
                 )
                 setAutoConfirmFeatureEnabled(true)
@@ -487,28 +491,31 @@
     fun hintedPinLength_withAutoConfirmPinAtRightLength_isSameLength() =
         testScope.runTest {
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 setAutoConfirmFeatureEnabled(true)
                 overrideCredential(
                     buildList {
-                        repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) }
+                        repeat(kosmos.fakeAuthenticationRepository.hintedPinLength) { add(it + 1) }
                     }
                 )
             }
 
-            assertThat(hintedPinLength).isEqualTo(utils.authenticationRepository.hintedPinLength)
+            assertThat(hintedPinLength)
+                .isEqualTo(kosmos.fakeAuthenticationRepository.hintedPinLength)
         }
 
     @Test
     fun hintedPinLength_withAutoConfirmPinTooLong_isNull() =
         testScope.runTest {
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
-            utils.authenticationRepository.apply {
+            kosmos.fakeAuthenticationRepository.apply {
                 setAuthenticationMethod(Pin)
                 overrideCredential(
                     buildList {
-                        repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) }
+                        repeat(kosmos.fakeAuthenticationRepository.hintedPinLength + 1) {
+                            add(it + 1)
+                        }
                     }
                 )
                 setAutoConfirmFeatureEnabled(true)
@@ -520,10 +527,10 @@
     @Test
     fun authenticate_withTooShortPassword() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
 
             val tooShortPassword = buildList {
-                repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
+                repeat(kosmos.fakeAuthenticationRepository.minPasswordLength - 1) { time ->
                     add("$time")
                 }
             }
@@ -534,7 +541,7 @@
         assertThat(authenticationResult).isEqualTo(AuthenticationResult.SUCCEEDED)
         assertThat(onAuthenticationResult).isTrue()
         assertThat(underTest.lockoutEndTimestamp).isNull()
-        assertThat(utils.authenticationRepository.lockoutStartedReportCount).isEqualTo(0)
+        assertThat(kosmos.fakeAuthenticationRepository.lockoutStartedReportCount).isEqualTo(0)
         assertThat(failedAuthenticationAttempts).isEqualTo(0)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 4a39799..72e884e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics
 
+import android.graphics.Bitmap
 import android.hardware.biometrics.BiometricManager.Authenticators
 import android.hardware.biometrics.ComponentInfoInternal
 import android.hardware.biometrics.PromptContentView
@@ -117,6 +118,8 @@
 }
 
 internal fun promptInfo(
+    logoRes: Int = -1,
+    logoBitmap: Bitmap? = null,
     title: String = "title",
     subtitle: String = "sub",
     description: String = "desc",
@@ -127,6 +130,8 @@
     negativeButton: String = "neg",
 ): PromptInfo {
     val info = PromptInfo()
+    info.logoRes = logoRes
+    info.logoBitmap = logoBitmap
     info.title = title
     info.subtitle = subtitle
     info.description = description
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt
index c2117ae..a67b093 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/data/repository/EmergencyServicesRepositoryImplTest.kt
@@ -20,8 +20,11 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.configurationRepository
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -36,8 +39,8 @@
 @RunWith(AndroidJUnit4::class)
 class EmergencyServicesRepositoryImplTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
 
     private lateinit var underTest: EmergencyServicesRepository
 
@@ -52,7 +55,7 @@
             EmergencyServicesRepository(
                 resources = context.resources,
                 applicationScope = testScope.backgroundScope,
-                configurationRepository = utils.configurationRepository,
+                configurationRepository = kosmos.configurationRepository,
             )
     }
 
@@ -71,7 +74,7 @@
 
     private fun TestScope.setEmergencyCallWhileSimLocked(isEnabled: Boolean) {
         overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, isEnabled)
-        utils.configurationRepository.onConfigurationChange()
+        kosmos.fakeConfigurationRepository.onConfigurationChange()
         runCurrent()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 63581b3..741cde8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -25,11 +25,18 @@
 import com.android.internal.logging.nano.MetricsProto
 import com.android.internal.util.emergencyAffordanceManager
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.whenever
 import com.android.telecom.telecomManager
@@ -54,11 +61,11 @@
     @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var telecomManager: TelecomManager
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val metricsLogger = utils.kosmos.fakeMetricsLogger
-    private val activityTaskManager = utils.kosmos.activityTaskManager
-    private val emergencyAffordanceManager = utils.kosmos.emergencyAffordanceManager
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val metricsLogger = kosmos.fakeMetricsLogger
+    private val activityTaskManager = kosmos.activityTaskManager
+    private val emergencyAffordanceManager = kosmos.emergencyAffordanceManager
 
     private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
 
@@ -68,9 +75,9 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        utils.fakeSceneContainerFlags.enabled = true
+        kosmos.fakeSceneContainerFlags.enabled = true
 
-        mobileConnectionsRepository = utils.mobileConnectionsRepository
+        mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
 
         overrideResource(R.string.lockscreen_emergency_call, MESSAGE_EMERGENCY_CALL)
         overrideResource(R.string.lockscreen_return_to_call, MESSAGE_RETURN_TO_CALL)
@@ -83,18 +90,18 @@
             .thenReturn(needsEmergencyAffordance)
         whenever(telecomManager.isInCall).thenReturn(false)
 
-        utils.fakeFeatureFlags.set(REFACTOR_GETCURRENTUSER, true)
+        kosmos.fakeFeatureFlagsClassic.set(REFACTOR_GETCURRENTUSER, true)
 
-        utils.telephonyRepository.setHasTelephonyRadio(true)
+        kosmos.fakeTelephonyRepository.setHasTelephonyRadio(true)
 
-        utils.kosmos.telecomManager = telecomManager
+        kosmos.telecomManager = telecomManager
     }
 
     @Test
     fun noTelephonyRadio_noButton() =
         testScope.runTest {
-            utils.telephonyRepository.setHasTelephonyRadio(false)
-            val underTest = utils.bouncerActionButtonInteractor()
+            kosmos.fakeTelephonyRepository.setHasTelephonyRadio(false)
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             assertThat(actionButton).isNull()
         }
@@ -102,8 +109,8 @@
     @Test
     fun noTelecomManager_noButton() =
         testScope.runTest {
-            utils.kosmos.telecomManager = null
-            val underTest = utils.bouncerActionButtonInteractor()
+            kosmos.telecomManager = null
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             assertThat(actionButton).isNull()
         }
@@ -111,9 +118,9 @@
     @Test
     fun duringCall_returnToCallButton() =
         testScope.runTest {
-            val underTest = utils.bouncerActionButtonInteractor()
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
-            utils.telephonyRepository.setIsInCall(true)
+            kosmos.fakeTelephonyRepository.setIsInCall(true)
 
             assertThat(actionButton).isNotNull()
             assertThat(actionButton?.label).isEqualTo(MESSAGE_RETURN_TO_CALL)
@@ -133,11 +140,13 @@
     @Test
     fun noCall_secureAuthMethod_emergencyCallButton() =
         testScope.runTest {
-            val underTest = utils.bouncerActionButtonInteractor()
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             mobileConnectionsRepository.isAnySimSecure.value = false
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.telephonyRepository.setIsInCall(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeTelephonyRepository.setIsInCall(false)
 
             assertThat(actionButton).isNotNull()
             assertThat(actionButton?.label).isEqualTo(MESSAGE_EMERGENCY_CALL)
@@ -163,11 +172,13 @@
     @Test
     fun noCall_insecureAuthMethodButSecureSim_emergencyCallButton() =
         testScope.runTest {
-            val underTest = utils.bouncerActionButtonInteractor()
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             mobileConnectionsRepository.isAnySimSecure.value = true
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-            utils.telephonyRepository.setIsInCall(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            kosmos.fakeTelephonyRepository.setIsInCall(false)
             runCurrent()
 
             assertThat(actionButton).isNotNull()
@@ -179,11 +190,13 @@
     @Test
     fun noCall_insecure_noButton() =
         testScope.runTest {
-            val underTest = utils.bouncerActionButtonInteractor()
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             mobileConnectionsRepository.isAnySimSecure.value = false
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-            utils.telephonyRepository.setIsInCall(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            kosmos.fakeTelephonyRepository.setIsInCall(false)
 
             assertThat(actionButton).isNull()
         }
@@ -191,13 +204,15 @@
     @Test
     fun noCall_simSecureButEmergencyNotSupported_noButton() =
         testScope.runTest {
-            val underTest = utils.bouncerActionButtonInteractor()
+            val underTest = kosmos.bouncerActionButtonInteractor
             val actionButton by collectLastValue(underTest.actionButton)
             mobileConnectionsRepository.isAnySimSecure.value = true
             overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, false)
-            utils.configurationRepository.onConfigurationChange()
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-            utils.telephonyRepository.setIsInCall(false)
+            kosmos.fakeConfigurationRepository.onConfigurationChange()
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            kosmos.fakeTelephonyRepository.setIsInCall(false)
             runCurrent()
 
             assertThat(actionButton).isNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 4b6199b..707777b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -20,15 +20,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.domain.interactor.AuthenticationResult
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,9 +50,9 @@
 @RunWith(AndroidJUnit4::class)
 class BouncerInteractorTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
-    private val testScope = utils.testScope
-    private val authenticationInteractor = utils.authenticationInteractor()
+    private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+    private val testScope = kosmos.testScope
+    private val authenticationInteractor = kosmos.authenticationInteractor
 
     private lateinit var underTest: BouncerInteractor
 
@@ -62,7 +67,7 @@
         overrideResource(R.string.kg_wrong_password, MESSAGE_WRONG_PASSWORD)
         overrideResource(R.string.kg_wrong_pattern, MESSAGE_WRONG_PATTERN)
 
-        underTest = utils.bouncerInteractor()
+        underTest = kosmos.bouncerInteractor
     }
 
     @Test
@@ -70,7 +75,9 @@
         testScope.runTest {
             val message by collectLastValue(underTest.message)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
             runCurrent()
             underTest.clearMessage()
             assertThat(message).isNull()
@@ -94,7 +101,9 @@
     @Test
     fun pinAuthMethod_sim_skipsAuthentication() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Sim
+            )
             runCurrent()
 
             // We rely on TelephonyManager to authenticate the sim card.
@@ -109,9 +118,11 @@
         testScope.runTest {
             val isAutoConfirmEnabled by collectLastValue(underTest.isAutoConfirmEnabled)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
             runCurrent()
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
             assertThat(isAutoConfirmEnabled).isTrue()
 
             // Incomplete input.
@@ -137,7 +148,9 @@
         testScope.runTest {
             val message by collectLastValue(underTest.message)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
             runCurrent()
 
             // Incomplete input.
@@ -160,7 +173,7 @@
     fun passwordAuthMethod() =
         testScope.runTest {
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
             runCurrent()
@@ -180,7 +193,8 @@
             assertThat(
                     underTest.authenticate(
                         buildList {
-                            repeat(utils.authenticationRepository.minPasswordLength - 1) { time ->
+                            repeat(kosmos.fakeAuthenticationRepository.minPasswordLength - 1) { time
+                                ->
                                 add("$time")
                             }
                         }
@@ -198,7 +212,7 @@
     fun patternAuthMethod() =
         testScope.runTest {
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pattern
             )
             runCurrent()
@@ -214,7 +228,8 @@
                     AuthenticationPatternCoordinate(0, 1),
                 )
             assertThat(wrongPattern).isNotEqualTo(FakeAuthenticationRepository.PATTERN)
-            assertThat(wrongPattern.size).isAtLeast(utils.authenticationRepository.minPatternLength)
+            assertThat(wrongPattern.size)
+                .isAtLeast(kosmos.fakeAuthenticationRepository.minPatternLength)
             assertThat(underTest.authenticate(wrongPattern)).isEqualTo(AuthenticationResult.FAILED)
             assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
 
@@ -225,7 +240,7 @@
             val tooShortPattern =
                 FakeAuthenticationRepository.PATTERN.subList(
                     0,
-                    utils.authenticationRepository.minPatternLength - 1
+                    kosmos.fakeAuthenticationRepository.minPatternLength - 1
                 )
             assertThat(underTest.authenticate(tooShortPattern))
                 .isEqualTo(AuthenticationResult.SKIPPED)
@@ -245,7 +260,9 @@
             val lockoutStartedEvents by collectValues(underTest.onLockoutStarted)
             val message by collectLastValue(underTest.message)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
             assertThat(lockoutStartedEvents).isEmpty()
 
             // Try the wrong PIN repeatedly, until lockout is triggered:
@@ -291,17 +308,17 @@
     @Test
     fun intentionalUserInputEvent_registersTouchEvent() =
         testScope.runTest {
-            assertThat(utils.powerRepository.userTouchRegistered).isFalse()
+            assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
             underTest.onIntentionalUserInput()
-            assertThat(utils.powerRepository.userTouchRegistered).isTrue()
+            assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
         }
 
     @Test
     fun intentionalUserInputEvent_notifiesFaceAuthInteractor() =
         testScope.runTest {
             val isFaceAuthRunning by
-                collectLastValue(utils.kosmos.fakeDeviceEntryFaceAuthRepository.isAuthRunning)
-            utils.kosmos.deviceEntryFaceAuthInteractor.onDeviceLifted()
+                collectLastValue(kosmos.fakeDeviceEntryFaceAuthRepository.isAuthRunning)
+            kosmos.deviceEntryFaceAuthInteractor.onDeviceLifted()
             runCurrent()
             assertThat(isFaceAuthRunning).isTrue()
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
index 8c53c0e..09fdd11 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/SimBouncerInteractorTest.kt
@@ -28,8 +28,11 @@
 import com.android.systemui.bouncer.data.repository.FakeSimBouncerRepository
 import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor.Companion.INVALID_SUBSCRIPTION_ID
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -54,10 +57,10 @@
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock lateinit var euiccManager: EuiccManager
 
-    private val utils = SceneTestUtils(this)
+    private val kosmos = testKosmos()
     private val bouncerSimRepository = FakeSimBouncerRepository()
     private val resources: Resources = context.resources
-    private val testScope = utils.testScope
+    private val testScope = kosmos.testScope
 
     private lateinit var underTest: SimBouncerInteractor
 
@@ -68,13 +71,13 @@
             SimBouncerInteractor(
                 context,
                 testScope.backgroundScope,
-                utils.testDispatcher,
+                kosmos.testDispatcher,
                 bouncerSimRepository,
                 telephonyManager,
                 resources,
                 keyguardUpdateMonitor,
                 euiccManager,
-                utils.mobileConnectionsRepository,
+                kosmos.mobileConnectionsRepository,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index 3043a71..27b84b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -20,9 +20,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
@@ -33,16 +37,16 @@
 @RunWith(AndroidJUnit4::class)
 class AuthMethodBouncerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val bouncerInteractor = utils.bouncerInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val bouncerInteractor = kosmos.bouncerInteractor
     private val underTest =
         PinBouncerViewModel(
             applicationContext = context,
             viewModelScope = testScope.backgroundScope,
             interactor = bouncerInteractor,
             isInputEnabled = MutableStateFlow(true),
-            simBouncerInteractor = utils.simBouncerInteractor,
+            simBouncerInteractor = kosmos.simBouncerInteractor,
             authenticationMethod = AuthenticationMethodModel.Pin,
         )
 
@@ -50,7 +54,9 @@
     fun animateFailure() =
         testScope.runTest {
             val animateFailure by collectLastValue(underTest.animateFailure)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
             assertThat(animateFailure).isFalse()
 
             // Wrong PIN:
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 4b1f9fe..cfe8c5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -20,15 +20,21 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlin.time.Duration.Companion.seconds
@@ -50,16 +56,16 @@
 @RunWith(AndroidJUnit4::class)
 class BouncerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val bouncerInteractor = utils.bouncerInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val bouncerInteractor = kosmos.bouncerInteractor
     private lateinit var underTest: BouncerViewModel
 
     @Before
     fun setUp() {
-        utils.fakeSceneContainerFlags.enabled = true
-        underTest = utils.bouncerViewModel()
+        kosmos.fakeSceneContainerFlags.enabled = true
+        underTest = kosmos.bouncerViewModel
     }
 
     @Test
@@ -68,7 +74,7 @@
             var authMethodViewModel: AuthMethodBouncerViewModel? = null
 
             authMethodsToTest().forEach { authMethod ->
-                utils.authenticationRepository.setAuthenticationMethod(authMethod)
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
                 val job =
                     underTest.authMethodViewModel.onEach { authMethodViewModel = it }.launchIn(this)
                 runCurrent()
@@ -98,13 +104,13 @@
 
             // First pass, populate our "seen" map:
             authMethodsToTest().forEach { authMethod ->
-                utils.authenticationRepository.setAuthenticationMethod(authMethod)
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
                 authMethodViewModel?.let { seen[authMethod] = it }
             }
 
             // Second pass, assert same instances are not reused:
             authMethodsToTest().forEach { authMethod ->
-                utils.authenticationRepository.setAuthenticationMethod(authMethod)
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
                 authMethodViewModel?.let {
                     assertThat(it.authenticationMethod).isEqualTo(authMethod)
                     assertThat(it).isNotSameInstanceAs(seen[authMethod])
@@ -116,11 +122,11 @@
     fun authMethodUnchanged_reusesInstances() =
         testScope.runTest {
             authMethodsToTest().forEach { authMethod ->
-                utils.authenticationRepository.setAuthenticationMethod(authMethod)
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
                 val firstInstance: AuthMethodBouncerViewModel? =
                     collectLastValue(underTest.authMethodViewModel).invoke()
 
-                utils.authenticationRepository.setAuthenticationMethod(authMethod)
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
                 val secondInstance: AuthMethodBouncerViewModel? =
                     collectLastValue(underTest.authMethodViewModel).invoke()
 
@@ -139,7 +145,7 @@
     fun message() =
         testScope.runTest {
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(message?.isUpdateAnimated).isTrue()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -157,8 +163,8 @@
         testScope.runTest {
             val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
             val message by collectLastValue(underTest.message)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(utils.authenticationRepository.lockoutEndTimestamp).isNull()
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            assertThat(kosmos.fakeAuthenticationRepository.lockoutEndTimestamp).isNull()
             assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) { times ->
@@ -192,7 +198,7 @@
                         authViewModel?.isInputEnabled ?: emptyFlow()
                     }
                 )
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isInputEnabled).isTrue()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -210,7 +216,7 @@
         testScope.runTest {
             val authMethodViewModel by collectLastValue(underTest.authMethodViewModel)
             val dialogViewModel by collectLastValue(underTest.dialogViewModel)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(authMethodViewModel?.lockoutMessageId).isNotNull()
 
             repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
@@ -228,17 +234,17 @@
     fun isSideBySideSupported() =
         testScope.runTest {
             val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isSideBySideSupported).isTrue()
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
             assertThat(isSideBySideSupported).isTrue()
 
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isSideBySideSupported).isTrue()
 
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
             assertThat(isSideBySideSupported).isFalse()
         }
 
@@ -246,12 +252,12 @@
     fun isFoldSplitRequired() =
         testScope.runTest {
             val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
-            utils.authenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
             assertThat(isFoldSplitRequired).isTrue()
-            utils.authenticationRepository.setAuthenticationMethod(Password)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
             assertThat(isFoldSplitRequired).isFalse()
 
-            utils.authenticationRepository.setAuthenticationMethod(Pattern)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pattern)
             assertThat(isFoldSplitRequired).isTrue()
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index 5c5632f..b3b6457 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -19,13 +19,19 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -43,12 +49,12 @@
 @RunWith(AndroidJUnit4::class)
 class PasswordBouncerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val bouncerInteractor = utils.bouncerInteractor()
-    private val bouncerViewModel = utils.bouncerViewModel()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val bouncerInteractor = kosmos.bouncerInteractor
+    private val bouncerViewModel = kosmos.bouncerViewModel
     private val isInputEnabled = MutableStateFlow(true)
 
     private val underTest =
@@ -140,10 +146,10 @@
         testScope.runTest {
             val message by collectLastValue(bouncerViewModel.message)
             val password by collectLastValue(underTest.password)
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
             switchToScene(SceneKey.Bouncer)
 
             // No input entered.
@@ -309,8 +315,10 @@
     }
 
     private fun TestScope.lockDeviceAndOpenPasswordBouncer() {
-        utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Password)
-        utils.deviceEntryRepository.setUnlocked(false)
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+            AuthenticationMethodModel.Password
+        )
+        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
         switchToScene(SceneKey.Bouncer)
     }
 
@@ -320,13 +328,13 @@
     ) {
         if (isLockedOut) {
             repeat(failedAttemptCount) {
-                utils.authenticationRepository.reportAuthenticationAttempt(false)
+                kosmos.fakeAuthenticationRepository.reportAuthenticationAttempt(false)
             }
-            utils.authenticationRepository.reportLockoutStarted(
+            kosmos.fakeAuthenticationRepository.reportLockoutStarted(
                 30.seconds.inWholeMilliseconds.toInt()
             )
         } else {
-            utils.authenticationRepository.reportAuthenticationAttempt(true)
+            kosmos.fakeAuthenticationRepository.reportAuthenticationAttempt(true)
         }
         isInputEnabled.value = !isLockedOut
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 9ee344a..c2680bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -20,13 +20,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.authenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,12 +51,12 @@
 @RunWith(AndroidJUnit4::class)
 class PatternBouncerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val bouncerInteractor = utils.bouncerInteractor()
-    private val bouncerViewModel = utils.bouncerViewModel()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val bouncerInteractor = kosmos.bouncerInteractor
+    private val bouncerViewModel = kosmos.bouncerViewModel
     private val underTest =
         PatternBouncerViewModel(
             applicationContext = context,
@@ -305,7 +312,7 @@
                 underTest.onDragStart()
                 CORRECT_PATTERN.subList(
                         0,
-                        utils.authenticationRepository.minPatternLength - 1,
+                        kosmos.authenticationRepository.minPatternLength - 1,
                     )
                     .forEach { coordinate ->
                         underTest.onDrag(
@@ -374,8 +381,10 @@
     }
 
     private fun TestScope.lockDeviceAndOpenPatternBouncer() {
-        utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pattern)
-        utils.deviceEntryRepository.setUnlocked(false)
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+            AuthenticationMethodModel.Pattern
+        )
+        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
         switchToScene(SceneKey.Bouncer)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 75e372f..1d660d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -20,12 +20,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,19 +51,19 @@
 @RunWith(AndroidJUnit4::class)
 class PinBouncerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val bouncerInteractor = utils.bouncerInteractor()
-    private val bouncerViewModel = utils.bouncerViewModel()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val bouncerInteractor = kosmos.bouncerInteractor
+    private val bouncerViewModel = kosmos.bouncerViewModel
     private val underTest =
         PinBouncerViewModel(
             applicationContext = context,
             viewModelScope = testScope.backgroundScope,
             interactor = bouncerInteractor,
             isInputEnabled = MutableStateFlow(true).asStateFlow(),
-            simBouncerInteractor = utils.simBouncerInteractor,
+            simBouncerInteractor = kosmos.simBouncerInteractor,
             authenticationMethod = AuthenticationMethodModel.Pin,
         )
 
@@ -86,7 +94,7 @@
                     viewModelScope = testScope.backgroundScope,
                     interactor = bouncerInteractor,
                     isInputEnabled = MutableStateFlow(true).asStateFlow(),
-                    simBouncerInteractor = utils.simBouncerInteractor,
+                    simBouncerInteractor = kosmos.simBouncerInteractor,
                     authenticationMethod = AuthenticationMethodModel.Sim,
                 )
 
@@ -97,7 +105,7 @@
     fun onErrorDialogDismissed_clearsDialogMessage() =
         testScope.runTest {
             val dialogMessage by collectLastValue(underTest.errorDialogMessage)
-            utils.simBouncerRepository.setSimVerificationErrorMessage("abc")
+            kosmos.fakeSimBouncerRepository.setSimVerificationErrorMessage("abc")
             assertThat(dialogMessage).isEqualTo("abc")
 
             underTest.onErrorDialogDismissed()
@@ -114,10 +122,10 @@
                     viewModelScope = testScope.backgroundScope,
                     interactor = bouncerInteractor,
                     isInputEnabled = MutableStateFlow(true).asStateFlow(),
-                    simBouncerInteractor = utils.simBouncerInteractor,
+                    simBouncerInteractor = kosmos.simBouncerInteractor,
                     authenticationMethod = AuthenticationMethodModel.Sim,
                 )
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
 
             assertThat(hintedPinLength).isNull()
@@ -254,7 +262,7 @@
     @Test
     fun onAutoConfirm_whenCorrect() =
         testScope.runTest {
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
             val authResult by collectLastValue(authenticationInteractor.onAuthenticationResult)
             lockDeviceAndOpenPinBouncer()
 
@@ -269,7 +277,7 @@
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
             val message by collectLastValue(bouncerViewModel.message)
             val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
             lockDeviceAndOpenPinBouncer()
 
             FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
@@ -309,7 +317,9 @@
         testScope.runTest {
             val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
 
             assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Shown)
         }
@@ -318,8 +328,10 @@
     fun backspaceButtonAppearance_withAutoConfirmButNoInput_isHidden() =
         testScope.runTest {
             val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
         }
@@ -328,8 +340,10 @@
     fun backspaceButtonAppearance_withAutoConfirmAndInput_isShownQuiet() =
         testScope.runTest {
             val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             underTest.onPinButtonClicked(1)
 
@@ -341,7 +355,9 @@
         testScope.runTest {
             val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
 
             assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Shown)
         }
@@ -350,8 +366,10 @@
     fun confirmButtonAppearance_withAutoConfirm_isHidden() =
         testScope.runTest {
             val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.authenticationRepository.setAutoConfirmFeatureEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
 
             assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden)
         }
@@ -361,10 +379,10 @@
         testScope.runTest {
             val isAnimationEnabled by collectLastValue(underTest.isDigitButtonAnimationEnabled)
 
-            utils.authenticationRepository.setPinEnhancedPrivacyEnabled(true)
+            kosmos.fakeAuthenticationRepository.setPinEnhancedPrivacyEnabled(true)
             assertThat(isAnimationEnabled).isFalse()
 
-            utils.authenticationRepository.setPinEnhancedPrivacyEnabled(false)
+            kosmos.fakeAuthenticationRepository.setPinEnhancedPrivacyEnabled(false)
             assertThat(isAnimationEnabled).isTrue()
         }
 
@@ -382,8 +400,8 @@
     }
 
     private fun TestScope.lockDeviceAndOpenPinBouncer() {
-        utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        utils.deviceEntryRepository.setUnlocked(false)
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
         switchToScene(SceneKey.Bouncer)
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
new file mode 100644
index 0000000..820bfbf
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.data.repository
+
+import android.content.SharedPreferences
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryImpl.Companion.FILE_NAME
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.FakeSharedPreferences
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalPrefsRepositoryImplTest : SysuiTestCase() {
+    private lateinit var underTest: CommunalPrefsRepositoryImpl
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private lateinit var userRepository: FakeUserRepository
+    private lateinit var userFileManager: UserFileManager
+
+    @Before
+    fun setUp() {
+        userRepository = kosmos.fakeUserRepository
+        userRepository.setUserInfos(USER_INFOS)
+
+        userFileManager =
+            FakeUserFileManager(
+                mapOf(
+                    USER_INFOS[0].id to FakeSharedPreferences(),
+                    USER_INFOS[1].id to FakeSharedPreferences()
+                )
+            )
+        underTest =
+            CommunalPrefsRepositoryImpl(
+                testScope.backgroundScope,
+                kosmos.testDispatcher,
+                userRepository,
+                userFileManager,
+            )
+    }
+
+    @Test
+    fun isCtaDismissedValue_byDefault_isFalse() =
+        testScope.runTest {
+            val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+            assertThat(isCtaDismissed).isFalse()
+        }
+
+    @Test
+    fun isCtaDismissedValue_onSet_isTrue() =
+        testScope.runTest {
+            val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+
+            underTest.setCtaDismissedForCurrentUser()
+            assertThat(isCtaDismissed).isTrue()
+        }
+
+    @Test
+    fun isCtaDismissedValue_whenSwitchUser() =
+        testScope.runTest {
+            val isCtaDismissed by collectLastValue(underTest.isCtaDismissed)
+            underTest.setCtaDismissedForCurrentUser()
+
+            // dismissed true for primary user
+            assertThat(isCtaDismissed).isTrue()
+
+            // switch to secondary user
+            userRepository.setSelectedUserInfo(USER_INFOS[1])
+
+            // dismissed is false for secondary user
+            assertThat(isCtaDismissed).isFalse()
+
+            // switch back to primary user
+            userRepository.setSelectedUserInfo(USER_INFOS[0])
+
+            // dismissed is true for primary user
+            assertThat(isCtaDismissed).isTrue()
+        }
+
+    private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+        UserFileManager {
+        override fun getFile(fileName: String, userId: Int): File {
+            throw UnsupportedOperationException()
+        }
+
+        override fun getSharedPreferences(
+            fileName: String,
+            mode: Int,
+            userId: Int
+        ): SharedPreferences {
+            if (fileName != FILE_NAME) {
+                throw IllegalArgumentException("Preference files must be $FILE_NAME")
+            }
+            return sharedPrefs.getValue(userId)
+        }
+    }
+
+    companion object {
+        val USER_INFOS =
+            listOf(
+                UserInfo(/* id= */ 0, "zero", /* flags= */ 0),
+                UserInfo(/* id= */ 1, "secondary", /* flags= */ 0),
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 65176e1..81d5344 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -24,11 +24,12 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.StandardTestDispatcher
@@ -51,8 +52,8 @@
 
     @Before
     fun setUp() {
-        val sceneTestUtils = SceneTestUtils(this)
-        sceneContainerRepository = sceneTestUtils.fakeSceneContainerRepository()
+        val kosmos = testKosmos()
+        sceneContainerRepository = kosmos.sceneContainerRepository
         featureFlagsClassic = FakeFeatureFlagsClassic()
 
         featureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index 4079f12..1c6cecd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.communal.data.repository
 
-import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo
 import android.content.ComponentName
@@ -32,6 +31,7 @@
 import com.android.systemui.communal.data.db.CommunalWidgetItem
 import com.android.systemui.communal.shared.CommunalWidgetHost
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.FakeLogBuffer
@@ -65,7 +65,7 @@
 
     @Mock private lateinit var appWidgetManager: AppWidgetManager
 
-    @Mock private lateinit var appWidgetHost: AppWidgetHost
+    @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
 
     @Mock private lateinit var userManager: UserManager
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index cd83c07..178eb6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -65,6 +66,7 @@
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
     private lateinit var smartspaceRepository: FakeSmartspaceRepository
     private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
     private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
 
     private lateinit var underTest: CommunalInteractor
@@ -84,6 +86,7 @@
         smartspaceRepository = withDeps.smartspaceRepository
         keyguardRepository = withDeps.keyguardRepository
         editWidgetsActivityStarter = withDeps.editWidgetsActivityStarter
+        communalPrefsRepository = withDeps.communalPrefsRepository
 
         underTest = withDeps.communalInteractor
     }
@@ -331,10 +334,9 @@
         }
 
     @Test
-    fun cta_visibilityTrue_shows() =
+    fun ctaTile_showsByDefault() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-            communalRepository.setCtaTileInViewModeVisibility(true)
 
             val ctaTileContent by collectLastValue(underTest.ctaTileContent)
 
@@ -346,10 +348,10 @@
         }
 
     @Test
-    fun ctaTile_visibilityFalse_doesNotShow() =
+    fun ctaTile_afterDismiss_doesNotShow() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-            communalRepository.setCtaTileInViewModeVisibility(false)
+            communalPrefsRepository.setCtaDismissedForCurrentUser()
 
             val ctaTileContent by collectLastValue(underTest.ctaTileContent)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
new file mode 100644
index 0000000..e904236
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
@@ -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.systemui.communal.ui.widgets
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalAppWidgetHostTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private lateinit var underTest: CommunalAppWidgetHost
+
+    @Before
+    fun setUp() {
+        underTest = CommunalAppWidgetHost(context = context, hostId = 116)
+    }
+
+    @Test
+    fun createViewForCommunal_returnCommunalAppWidgetView() =
+        testScope.runTest {
+            val appWidgetId = 789
+            val view =
+                underTest.createViewForCommunal(
+                    context = context,
+                    appWidgetId = appWidgetId,
+                    appWidget = null
+                )
+            assertThat(view).isInstanceOf(CommunalAppWidgetHostView::class.java)
+            assertThat(view).isNotNull()
+            assertThat(view.appWidgetId).isEqualTo(appWidgetId)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 54510a8..09243e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -19,7 +19,6 @@
 import android.app.Activity.RESULT_CANCELED
 import android.app.Activity.RESULT_OK
 import android.app.smartspace.SmartspaceTarget
-import android.appwidget.AppWidgetHost
 import android.content.ComponentName
 import android.provider.Settings
 import android.widget.RemoteViews
@@ -36,6 +35,7 @@
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.kosmos.testScope
@@ -61,7 +61,7 @@
 @RunWith(AndroidJUnit4::class)
 class CommunalEditModeViewModelTest : SysuiTestCase() {
     @Mock private lateinit var mediaHost: MediaHost
-    @Mock private lateinit var appWidgetHost: AppWidgetHost
+    @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost
     @Mock private lateinit var uiEventLogger: UiEventLogger
 
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 804c052..f9cfc37 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
@@ -66,6 +67,7 @@
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
     private lateinit var smartspaceRepository: FakeSmartspaceRepository
     private lateinit var mediaRepository: FakeCommunalMediaRepository
+    private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
 
     private lateinit var underTest: CommunalViewModel
 
@@ -82,6 +84,7 @@
         widgetRepository = withDeps.widgetRepository
         smartspaceRepository = withDeps.smartspaceRepository
         mediaRepository = withDeps.mediaRepository
+        communalPrefsRepository = withDeps.communalPrefsRepository
 
         underTest =
             CommunalViewModel(
@@ -149,9 +152,6 @@
             // Media playing.
             mediaRepository.mediaActive()
 
-            // CTA Tile not dismissed.
-            communalRepository.setCtaTileInViewModeVisibility(true)
-
             val communalContent by collectLastValue(underTest.communalContent)
 
             // Order is smart space, then UMO, widget content and cta tile.
@@ -171,7 +171,6 @@
     fun dismissCta_hidesCtaTileAndShowsPopup_thenHidesPopupAfterTimeout() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
-            communalRepository.setCtaTileInViewModeVisibility(true)
 
             val communalContent by collectLastValue(underTest.communalContent)
             val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing)
@@ -195,7 +194,6 @@
     fun popup_onDismiss_hidesImmediately() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
-            communalRepository.setCtaTileInViewModeVisibility(true)
 
             val isPopupOnDismissCtaShowing by collectLastValue(underTest.isPopupOnDismissCtaShowing)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
index 565049b..b54c5bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryTest.kt
@@ -7,9 +7,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.whenever
@@ -36,8 +38,8 @@
     @Mock private lateinit var keyguardBypassController: KeyguardBypassController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
 
-    private val testUtils = SceneTestUtils(this)
-    private val testScope = testUtils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
     private val userRepository = FakeUserRepository()
     private val keyguardRepository = FakeKeyguardRepository()
 
@@ -52,7 +54,7 @@
         underTest =
             DeviceEntryRepositoryImpl(
                 applicationScope = testScope.backgroundScope,
-                backgroundDispatcher = testUtils.testDispatcher,
+                backgroundDispatcher = kosmos.testDispatcher,
                 userRepository = userRepository,
                 lockPatternUtils = lockPatternUtils,
                 keyguardBypassController = keyguardBypassController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
index 929e879..62d2315 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorTest.kt
@@ -20,14 +20,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeTrustRepository
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -41,18 +47,18 @@
 @RunWith(AndroidJUnit4::class)
 class DeviceEntryInteractorTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val faceAuthRepository = utils.kosmos.fakeDeviceEntryFaceAuthRepository
-    private val trustRepository = utils.kosmos.fakeTrustRepository
-    private val sceneInteractor = utils.sceneInteractor()
-    private val authenticationInteractor = utils.authenticationInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
+    private val trustRepository = kosmos.fakeTrustRepository
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val authenticationInteractor = kosmos.authenticationInteractor
     private lateinit var underTest: DeviceEntryInteractor
 
     @Before
     fun setUp() {
-        utils.fakeSceneContainerFlags.enabled = true
-        underTest = utils.deviceEntryInteractor()
+        kosmos.fakeSceneContainerFlags.enabled = true
+        underTest = kosmos.deviceEntryInteractor
     }
 
     @Test
@@ -65,8 +71,10 @@
     @Test
     fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-            utils.deviceEntryRepository.apply {
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            kosmos.fakeDeviceEntryRepository.apply {
                 setLockscreenEnabled(false)
 
                 // Toggle isUnlocked, twice.
@@ -99,8 +107,10 @@
     @Test
     fun isUnlocked_whenAuthMethodIsSimAndUnlocked_isFalse() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Sim
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             val isUnlocked by collectLastValue(underTest.isUnlocked)
             assertThat(isUnlocked).isFalse()
@@ -157,10 +167,10 @@
     @Test
     fun isDeviceEntered_onBouncer_isFalse() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pattern
             )
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             switchToScene(SceneKey.Lockscreen)
             runCurrent()
             switchToScene(SceneKey.Bouncer)
@@ -182,8 +192,10 @@
     @Test
     fun canSwipeToEnter_onLockscreenWithPin_isFalse() =
         testScope.runTest {
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             switchToScene(SceneKey.Lockscreen)
 
             val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
@@ -203,15 +215,15 @@
         }
 
     private fun setupSwipeDeviceEntryMethod() {
-        utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-        utils.deviceEntryRepository.setLockscreenEnabled(true)
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+        kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
     }
 
     @Test
     fun canSwipeToEnter_whenTrustedByTrustManager_isTrue() =
         testScope.runTest {
             val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
             switchToScene(SceneKey.Lockscreen)
@@ -228,7 +240,7 @@
     fun canSwipeToEnter_whenAuthenticatedByFace_isTrue() =
         testScope.runTest {
             val canSwipeToEnter by collectLastValue(underTest.canSwipeToEnter)
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
             switchToScene(SceneKey.Lockscreen)
@@ -244,9 +256,9 @@
     @Test
     fun isAuthenticationRequired_lockedAndSecured_true() =
         testScope.runTest {
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
             runCurrent()
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
 
@@ -256,9 +268,11 @@
     @Test
     fun isAuthenticationRequired_lockedAndNotSecured_false() =
         testScope.runTest {
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
             runCurrent()
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
 
             assertThat(underTest.isAuthenticationRequired()).isFalse()
         }
@@ -266,9 +280,9 @@
     @Test
     fun isAuthenticationRequired_unlockedAndSecured_false() =
         testScope.runTest {
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
-            utils.authenticationRepository.setAuthenticationMethod(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Password
             )
 
@@ -278,9 +292,11 @@
     @Test
     fun isAuthenticationRequired_unlockedAndNotSecured_false() =
         testScope.runTest {
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
 
             assertThat(underTest.isAuthenticationRequired()).isFalse()
         }
@@ -288,7 +304,7 @@
     @Test
     fun isBypassEnabled_enabledInRepository_true() =
         testScope.runTest {
-            utils.deviceEntryRepository.setBypassEnabled(true)
+            kosmos.fakeDeviceEntryRepository.setBypassEnabled(true)
             assertThat(underTest.isBypassEnabled.value).isTrue()
         }
 
@@ -299,8 +315,10 @@
             switchToScene(SceneKey.Lockscreen)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
 
             underTest.attemptDeviceEntry()
@@ -315,7 +333,9 @@
             switchToScene(SceneKey.Lockscreen)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
 
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
 
             underTest.attemptDeviceEntry()
 
@@ -329,8 +349,10 @@
             switchToScene(SceneKey.Lockscreen)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
 
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
 
             underTest.attemptDeviceEntry()
 
@@ -340,7 +362,7 @@
     @Test
     fun isBypassEnabled_disabledInRepository_false() =
         testScope.runTest {
-            utils.deviceEntryRepository.setBypassEnabled(false)
+            kosmos.fakeDeviceEntryRepository.setBypassEnabled(false)
             assertThat(underTest.isBypassEnabled.value).isFalse()
         }
 
@@ -348,8 +370,10 @@
     fun successfulAuthenticationChallengeAttempt_updatesIsUnlockedState() =
         testScope.runTest {
             val isUnlocked by collectLastValue(underTest.isUnlocked)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             assertThat(isUnlocked).isFalse()
 
             authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 8933d2c..2c3afb1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -26,12 +26,16 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -48,10 +52,10 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardInteractorTest : SysuiTestCase() {
 
-    private val testUtils = SceneTestUtils(this)
-    private val testScope = testUtils.testScope
-    private val repository = testUtils.keyguardRepository
-    private val sceneInteractor = testUtils.sceneInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val repository = kosmos.fakeKeyguardRepository
+    private val sceneInteractor = kosmos.sceneInteractor
     private val commandQueue = FakeCommandQueue()
     private val bouncerRepository = FakeKeyguardBouncerRepository()
     private val shadeRepository = FakeShadeRepository()
@@ -63,7 +67,7 @@
             repository = repository,
             commandQueue = commandQueue,
             powerInteractor = PowerInteractorFactory.create().powerInteractor,
-            sceneContainerFlags = testUtils.fakeSceneContainerFlags,
+            sceneContainerFlags = kosmos.fakeSceneContainerFlags,
             bouncerRepository = bouncerRepository,
             configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
             shadeRepository = shadeRepository,
@@ -183,7 +187,7 @@
     @Test
     fun animationDozingTransitions() =
         testScope.runTest {
-            testUtils.fakeSceneContainerFlags.enabled = true
+            kosmos.fakeSceneContainerFlags.enabled = true
             val isAnimate by collectLastValue(underTest.animateDozingTransitions)
 
             underTest.setAnimateDozingTransitions(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 04e90c8..aa15d0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -21,11 +21,19 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,9 +45,9 @@
 @RunWith(AndroidJUnit4::class)
 class LockscreenSceneViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
 
     private val underTest = createLockscreenSceneViewModel()
 
@@ -47,9 +55,11 @@
     fun upTransitionSceneKey_canSwipeToUnlock_gone() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
 
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
@@ -59,8 +69,10 @@
     fun upTransitionSceneKey_cannotSwipeToUnlock_bouncer() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
             sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
 
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -69,7 +81,7 @@
     @Test
     fun leftTransitionSceneKey_communalIsEnabled_communal() =
         testScope.runTest {
-            utils.communalRepository.setIsCommunalEnabled(true)
+            kosmos.fakeCommunalRepository.setIsCommunalEnabled(true)
             val underTest = createLockscreenSceneViewModel()
 
             assertThat(underTest.leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
@@ -78,7 +90,7 @@
     @Test
     fun leftTransitionSceneKey_communalIsDisabled_null() =
         testScope.runTest {
-            utils.communalRepository.setIsCommunalEnabled(false)
+            kosmos.fakeCommunalRepository.setIsCommunalEnabled(false)
             val underTest = createLockscreenSceneViewModel()
 
             assertThat(underTest.leftDestinationSceneKey).isNull()
@@ -87,13 +99,13 @@
     private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
         return LockscreenSceneViewModel(
             applicationScope = testScope.backgroundScope,
-            deviceEntryInteractor = utils.deviceEntryInteractor(),
-            communalInteractor = utils.communalInteractor(),
+            deviceEntryInteractor = kosmos.deviceEntryInteractor,
+            communalInteractor = kosmos.communalInteractor,
             longPress =
                 KeyguardLongPressViewModel(
                     interactor = mock(),
                 ),
-            notifications = utils.notificationsPlaceholderViewModel(),
+            notifications = kosmos.notificationsPlaceholderViewModel,
         )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 6403ed1..be523b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -22,13 +22,15 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Direction
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.scene.shared.model.UserAction
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -36,6 +38,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
@@ -47,9 +50,9 @@
 @RunWith(AndroidJUnit4::class)
 class QuickSettingsSceneViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
     private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
     private val qsFlexiglassAdapter = FakeQSSceneAdapter { mock() }
@@ -90,7 +93,7 @@
             QuickSettingsSceneViewModel(
                 shadeHeaderViewModel = shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
-                notifications = utils.notificationsPlaceholderViewModel(),
+                notifications = kosmos.notificationsPlaceholderViewModel,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index ecc2ef1..a08283d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -27,22 +27,39 @@
 import com.android.internal.util.emergencyAffordanceManager
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
+import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.model.SysUiState
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
@@ -50,13 +67,17 @@
 import com.android.systemui.settings.FakeDisplayTracker
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -101,13 +122,13 @@
 @RunWith(AndroidJUnit4::class)
 class SceneFrameworkIntegrationTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
-    private val testScope = utils.testScope
-    private val sceneContainerConfig = utils.fakeSceneContainerConfig()
-    private val sceneInteractor = utils.sceneInteractor()
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val deviceEntryInteractor = utils.deviceEntryInteractor()
-    private val communalInteractor = utils.communalInteractor()
+    private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+    private val testScope = kosmos.testScope
+    private val sceneContainerConfig = kosmos.sceneContainerConfig
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val deviceEntryInteractor = kosmos.deviceEntryInteractor
+    private val communalInteractor = kosmos.communalInteractor
 
     private val transitionState =
         MutableStateFlow<ObservableTransitionState>(
@@ -116,11 +137,11 @@
     private val sceneContainerViewModel =
         SceneContainerViewModel(
                 sceneInteractor = sceneInteractor,
-                falsingInteractor = utils.falsingInteractor(),
+                falsingInteractor = kosmos.falsingInteractor,
             )
             .apply { setTransitionState(transitionState) }
 
-    private val bouncerInteractor = utils.bouncerInteractor()
+    private val bouncerInteractor = kosmos.bouncerInteractor
 
     private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
     private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
@@ -135,7 +156,7 @@
                 KeyguardLongPressViewModel(
                     interactor = mock(),
                 ),
-            notifications = utils.notificationsPlaceholderViewModel(),
+            notifications = kosmos.notificationsPlaceholderViewModel,
         )
 
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
@@ -152,21 +173,22 @@
                     FakeMobileConnectionsRepository(),
                 ),
             constants = mock(),
-            utils.fakeFeatureFlags,
+            flags = kosmos.fakeFeatureFlagsClassic,
             scope = testScope.backgroundScope,
         )
 
     private lateinit var shadeHeaderViewModel: ShadeHeaderViewModel
     private lateinit var shadeSceneViewModel: ShadeSceneViewModel
 
-    private val keyguardInteractor = utils.keyguardInteractor()
-    private val powerInteractor = utils.powerInteractor()
+    private val keyguardInteractor = kosmos.keyguardInteractor
+    private val powerInteractor = kosmos.powerInteractor
 
     private var bouncerSceneJob: Job? = null
 
     private val qsFlexiglassAdapter = FakeQSSceneAdapter(inflateDelegate = { mock() })
 
     @Mock private lateinit var mediaDataManager: MediaDataManager
+
     @Mock private lateinit var mediaHost: MediaHost
 
     private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
@@ -177,27 +199,27 @@
         MockitoAnnotations.initMocks(this)
 
         overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
-        telecomManager = checkNotNull(utils.kosmos.telecomManager)
+        telecomManager = checkNotNull(kosmos.telecomManager)
         whenever(telecomManager.isInCall).thenReturn(false)
-        emergencyAffordanceManager = utils.kosmos.emergencyAffordanceManager
+        emergencyAffordanceManager = kosmos.emergencyAffordanceManager
         whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
 
-        utils.fakeFeatureFlags.apply {
+        kosmos.fakeFeatureFlagsClassic.apply {
             set(Flags.NEW_NETWORK_SLICE_UI, false)
             set(Flags.REFACTOR_GETCURRENTUSER, true)
         }
 
-        mobileConnectionsRepository = utils.mobileConnectionsRepository
+        mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
         mobileConnectionsRepository.isAnySimSecure.value = false
 
-        utils.telephonyRepository.apply {
+        kosmos.fakeTelephonyRepository.apply {
             setHasTelephonyRadio(true)
             setCallState(TelephonyManager.CALL_STATE_IDLE)
             setIsInCall(false)
         }
 
-        bouncerActionButtonInteractor = utils.bouncerActionButtonInteractor()
-        bouncerViewModel = utils.bouncerViewModel()
+        bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
+        bouncerViewModel = kosmos.bouncerViewModel
 
         shadeHeaderViewModel =
             ShadeHeaderViewModel(
@@ -215,12 +237,12 @@
                 deviceEntryInteractor = deviceEntryInteractor,
                 shadeHeaderViewModel = shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
-                notifications = utils.notificationsPlaceholderViewModel(),
+                notifications = kosmos.notificationsPlaceholderViewModel,
                 mediaDataManager = mediaDataManager,
                 mediaHost = mediaHost,
             )
 
-        utils.deviceEntryRepository.setUnlocked(false)
+        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
 
         val displayTracker = FakeDisplayTracker(context)
         val sysUiState = SysUiState(displayTracker)
@@ -230,15 +252,15 @@
                 sceneInteractor = sceneInteractor,
                 deviceEntryInteractor = deviceEntryInteractor,
                 keyguardInteractor = keyguardInteractor,
-                flags = utils.fakeSceneContainerFlags,
+                flags = kosmos.fakeSceneContainerFlags,
                 sysUiState = sysUiState,
                 displayId = displayTracker.defaultDisplayId,
                 sceneLogger = mock(),
-                falsingCollector = utils.falsingCollector(),
+                falsingCollector = kosmos.falsingCollector,
                 powerInteractor = powerInteractor,
                 bouncerInteractor = bouncerInteractor,
-                simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
-                authenticationInteractor = dagger.Lazy { utils.authenticationInteractor() },
+                simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
+                authenticationInteractor = dagger.Lazy { kosmos.authenticationInteractor },
                 windowController = mock(),
             )
         startable.start()
@@ -534,15 +556,15 @@
         // Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
         // lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
         // is not an observable that can trigger a new evaluation.
-        utils.deviceEntryRepository.setLockscreenEnabled(enableLockscreen)
-        utils.authenticationRepository.setAuthenticationMethod(authMethod)
+        kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
         runCurrent()
     }
 
     /** Emulates a phone call in progress. */
     private fun TestScope.startPhoneCall() {
         whenever(telecomManager.isInCall).thenReturn(true)
-        utils.telephonyRepository.apply {
+        kosmos.fakeTelephonyRepository.apply {
             setHasTelephonyRadio(true)
             setIsInCall(true)
             setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
@@ -651,7 +673,7 @@
             .that(authMethod.isSecure)
             .isTrue()
 
-        utils.deviceEntryRepository.setUnlocked(false)
+        kosmos.fakeDeviceEntryRepository.setUnlocked(false)
         runCurrent()
     }
 
@@ -665,7 +687,7 @@
         enterPin()
         // This repository state is not changed by the AuthInteractor, it relies on
         // KeyguardStateController.
-        utils.deviceEntryRepository.setUnlocked(true)
+        kosmos.fakeDeviceEntryRepository.setUnlocked(true)
         emulateUiSceneTransition(
             expectedVisible = false,
         )
@@ -721,7 +743,7 @@
         }
         pinBouncerViewModel.onAuthenticateButtonClicked()
         setAuthMethod(authMethodAfterSimUnlock)
-        utils.mobileConnectionsRepository.isAnySimSecure.value = false
+        kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
         runCurrent()
     }
 
@@ -768,7 +790,7 @@
 
     private fun TestScope.introduceLockedSim() {
         setAuthMethod(AuthenticationMethodModel.Sim)
-        utils.mobileConnectionsRepository.isAnySimSecure.value = true
+        kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
         runCurrent()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 339d026..b267720 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -22,11 +22,14 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.sceneKeys
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,12 +42,12 @@
 @RunWith(AndroidJUnit4::class)
 class SceneContainerRepositoryTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this).apply { fakeSceneContainerFlags.enabled = true }
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos().apply { fakeSceneContainerFlags.enabled = true }
+    private val testScope = kosmos.testScope
 
     @Test
     fun allSceneKeys() {
-        val underTest = utils.fakeSceneContainerRepository()
+        val underTest = kosmos.sceneContainerRepository
         assertThat(underTest.allSceneKeys())
             .isEqualTo(
                 listOf(
@@ -61,7 +64,7 @@
     @Test
     fun desiredScene() =
         testScope.runTest {
-            val underTest = utils.fakeSceneContainerRepository()
+            val underTest = kosmos.sceneContainerRepository
             val currentScene by collectLastValue(underTest.desiredScene)
             assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
 
@@ -71,15 +74,15 @@
 
     @Test(expected = IllegalStateException::class)
     fun setDesiredScene_noSuchSceneInContainer_throws() {
-        utils.kosmos.sceneKeys = listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)
-        val underTest = utils.fakeSceneContainerRepository()
+        kosmos.sceneKeys = listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)
+        val underTest = kosmos.sceneContainerRepository
         underTest.setDesiredScene(SceneModel(SceneKey.Shade))
     }
 
     @Test
     fun isVisible() =
         testScope.runTest {
-            val underTest = utils.fakeSceneContainerRepository()
+            val underTest = kosmos.sceneContainerRepository
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isTrue()
 
@@ -93,19 +96,19 @@
     @Test
     fun transitionState_defaultsToIdle() =
         testScope.runTest {
-            val underTest = utils.fakeSceneContainerRepository()
+            val underTest = kosmos.sceneContainerRepository
             val transitionState by collectLastValue(underTest.transitionState)
 
             assertThat(transitionState)
                 .isEqualTo(
-                    ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
+                    ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
                 )
         }
 
     @Test
     fun transitionState_reflectsUpdates() =
         testScope.runTest {
-            val underTest = utils.fakeSceneContainerRepository()
+            val underTest = kosmos.sceneContainerRepository
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(SceneKey.Lockscreen)
@@ -134,7 +137,7 @@
             underTest.setTransitionState(null)
             assertThat(reflectedTransitionState)
                 .isEqualTo(
-                    ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
+                    ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
                 )
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 486f7ab..d159986 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -20,10 +20,16 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.sceneContainerConfig
+import com.android.systemui.scene.sceneKeys
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
@@ -36,20 +42,20 @@
 @RunWith(AndroidJUnit4::class)
 class SceneInteractorTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
 
     private lateinit var underTest: SceneInteractor
 
     @Before
     fun setUp() {
-        utils.fakeSceneContainerFlags.enabled = true
-        underTest = utils.sceneInteractor()
+        kosmos.fakeSceneContainerFlags.enabled = true
+        underTest = kosmos.sceneInteractor
     }
 
     @Test
     fun allSceneKeys() {
-        assertThat(underTest.allSceneKeys()).isEqualTo(utils.fakeSceneKeys())
+        assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys)
     }
 
     @Test
@@ -75,7 +81,7 @@
     @Test
     fun transitionState() =
         testScope.runTest {
-            val underTest = utils.fakeSceneContainerRepository()
+            val underTest = kosmos.sceneContainerRepository
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(SceneKey.Lockscreen)
@@ -104,7 +110,7 @@
             underTest.setTransitionState(null)
             assertThat(reflectedTransitionState)
                 .isEqualTo(
-                    ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
+                    ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
                 )
         }
 
@@ -348,8 +354,8 @@
     @Test
     fun userInput() =
         testScope.runTest {
-            assertThat(utils.powerRepository.userTouchRegistered).isFalse()
+            assertThat(kosmos.fakePowerRepository.userTouchRegistered).isFalse()
             underTest.onUserInput()
-            assertThat(utils.powerRepository.userTouchRegistered).isTrue()
+            assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
index 8be4eeb..f23716c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.scene.domain.interactor
 
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.IStatusBarService
@@ -28,7 +30,11 @@
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
 import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
 import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.init.NotificationsController
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -37,6 +43,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 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
@@ -50,6 +57,7 @@
 class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
 
     private val testScope = TestScope()
+    private val testDispatcher = StandardTestDispatcher()
     private val iStatusBarService = mock<IStatusBarService>()
     private val executor = FakeExecutor(FakeSystemClock())
     private val windowRootViewVisibilityRepository =
@@ -59,6 +67,9 @@
     private val notificationPresenter = mock<NotificationPresenter>()
     private val notificationsController = mock<NotificationsController>()
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+    private val activeNotificationsRepository = ActiveNotificationListRepository()
+    private val activeNotificationsInteractor =
+        ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher)
 
     private val underTest =
         WindowRootViewVisibilityInteractor(
@@ -67,6 +78,7 @@
                 keyguardRepository,
                 headsUpManager,
                 powerInteractor,
+                activeNotificationsInteractor,
             )
             .apply { setUp(notificationPresenter, notificationsController) }
 
@@ -257,7 +269,8 @@
         }
 
     @Test
-    fun lockscreenShadeInteractive_hasHeadsUpAndNotifPresenterCollapsed_notifCountOne() =
+    @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_hasHeadsUpAndNotifPresenterCollapsed_flagOff_notifCountOne() =
         testScope.runTest {
             underTest.start()
 
@@ -273,6 +286,23 @@
         }
 
     @Test
+    @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_hasHeadsUpAndNotifPresenterCollapsed_flagOn_notifCountOne() =
+        testScope.runTest {
+            underTest.start()
+
+            whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+            whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+            activeNotificationsRepository.setActiveNotifs(4)
+
+            makeLockscreenShadeVisible()
+
+            val notifCount = argumentCaptor<Int>()
+            verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+            assertThat(notifCount.value).isEqualTo(1)
+        }
+
+    @Test
     fun lockscreenShadeInteractive_hasHeadsUpAndNullPresenter_notifCountOne() =
         testScope.runTest {
             underTest.start()
@@ -288,7 +318,8 @@
         }
 
     @Test
-    fun lockscreenShadeInteractive_noHeadsUp_notifCountMatchesNotifController() =
+    @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_noHeadsUp_flagOff_notifCountMatchesNotifController() =
         testScope.runTest {
             underTest.start()
             whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
@@ -304,7 +335,25 @@
         }
 
     @Test
-    fun lockscreenShadeInteractive_notifPresenterNotCollapsed_notifCountMatchesNotifController() =
+    @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_noHeadsUp_flagOn_notifCountMatchesNotifController() =
+        testScope.runTest {
+            underTest.start()
+            whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+            whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+            activeNotificationsRepository.setActiveNotifs(9)
+
+            makeLockscreenShadeVisible()
+
+            val notifCount = argumentCaptor<Int>()
+            verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+            assertThat(notifCount.value).isEqualTo(9)
+        }
+
+    @Test
+    @DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_notifPresenterNotCollapsed_flagOff_notifCountMatchesNotifController() =
         testScope.runTest {
             underTest.start()
             whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
@@ -320,6 +369,23 @@
         }
 
     @Test
+    @EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+    fun lockscreenShadeInteractive_notifPresenterNotCollapsed_flagOn_notifCountMatchesNotifController() =
+        testScope.runTest {
+            underTest.start()
+            whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+
+            whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+            activeNotificationsRepository.setActiveNotifs(8)
+
+            makeLockscreenShadeVisible()
+
+            val notifCount = argumentCaptor<Int>()
+            verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+            assertThat(notifCount.value).isEqualTo(8)
+        }
+
+    @Test
     fun lockscreenShadeInteractive_noHeadsUp_noNotifController_notifCountZero() =
         testScope.runTest {
             underTest.start()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 5fe4ca1..4afa5f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -25,19 +25,31 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.SysUiState
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
 import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -65,15 +77,15 @@
 
     @Mock private lateinit var windowController: NotificationShadeWindowController
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
-    private val sceneContainerFlags = utils.fakeSceneContainerFlags
-    private val authenticationInteractor = utils.authenticationInteractor()
-    private val bouncerInteractor = utils.bouncerInteractor()
-    private val faceAuthRepository = utils.kosmos.fakeDeviceEntryFaceAuthRepository
-    private val deviceEntryInteractor = utils.deviceEntryInteractor()
-    private val keyguardInteractor = utils.keyguardInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val sceneContainerFlags = kosmos.fakeSceneContainerFlags
+    private val authenticationInteractor = kosmos.authenticationInteractor
+    private val bouncerInteractor = kosmos.bouncerInteractor
+    private val faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository
+    private val deviceEntryInteractor = kosmos.deviceEntryInteractor
+    private val keyguardInteractor = kosmos.keyguardInteractor
     private val sysUiState: SysUiState = mock()
     private val falsingCollector: FalsingCollector = mock()
     private val powerInteractor = PowerInteractorFactory.create().powerInteractor
@@ -97,7 +109,7 @@
                 falsingCollector = falsingCollector,
                 powerInteractor = powerInteractor,
                 bouncerInteractor = bouncerInteractor,
-                simBouncerInteractor = dagger.Lazy { utils.simBouncerInteractor },
+                simBouncerInteractor = dagger.Lazy { kosmos.simBouncerInteractor },
                 authenticationInteractor = dagger.Lazy { authenticationInteractor },
                 windowController = windowController,
             )
@@ -172,7 +184,7 @@
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
             underTest.start()
 
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
         }
@@ -188,7 +200,7 @@
             assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
             underTest.start()
 
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
         }
@@ -204,7 +216,7 @@
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
             underTest.start()
 
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
         }
@@ -222,7 +234,7 @@
 
             // Authenticate using a passive auth method like face auth while bypass is disabled.
             faceAuthRepository.isAuthenticated.value = true
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
         }
@@ -244,7 +256,7 @@
             transitionStateFlowValue.value = ObservableTransitionState.Idle(SceneKey.Shade)
             assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
 
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Shade)
@@ -263,7 +275,7 @@
 
             // Authenticate using a passive auth method like face auth while bypass is disabled.
             faceAuthRepository.isAuthenticated.value = true
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
         }
@@ -373,7 +385,7 @@
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
             underTest.start()
 
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
             powerInteractor.setAwakeForTest()
             runCurrent()
@@ -463,11 +475,11 @@
             runCurrent()
             verify(falsingCollector).setShowingAod(false)
 
-            utils.keyguardRepository.setIsDozing(true)
+            kosmos.fakeKeyguardRepository.setIsDozing(true)
             runCurrent()
             verify(falsingCollector).setShowingAod(true)
 
-            utils.keyguardRepository.setIsDozing(false)
+            kosmos.fakeKeyguardRepository.setIsDozing(false)
             runCurrent()
             verify(falsingCollector, times(2)).setShowingAod(false)
         }
@@ -493,7 +505,7 @@
     @Test
     fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
         testScope.runTest {
-            utils.keyguardRepository.setAodAvailable(false)
+            kosmos.fakeKeyguardRepository.setAodAvailable(false)
             runCurrent()
             prepareState(
                 initialSceneKey = SceneKey.Lockscreen,
@@ -541,7 +553,7 @@
     @Test
     fun collectFalsingSignals_screenOnAndOff_aodAvailable() =
         testScope.runTest {
-            utils.keyguardRepository.setAodAvailable(true)
+            kosmos.fakeKeyguardRepository.setAodAvailable(true)
             runCurrent()
             prepareState(
                 initialSceneKey = SceneKey.Lockscreen,
@@ -619,7 +631,7 @@
             underTest.start()
             runCurrent()
 
-            utils.mobileConnectionsRepository.isAnySimSecure.value = true
+            kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
@@ -628,7 +640,7 @@
     @Test
     fun switchesToLockscreen_whenSimBecomesUnlocked() =
         testScope.runTest {
-            utils.mobileConnectionsRepository.isAnySimSecure.value = true
+            kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
             val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
 
             prepareState(
@@ -638,7 +650,7 @@
             )
             underTest.start()
             runCurrent()
-            utils.mobileConnectionsRepository.isAnySimSecure.value = false
+            kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
@@ -647,7 +659,7 @@
     @Test
     fun switchesToGone_whenSimBecomesUnlocked_ifDeviceUnlockedAndLockscreenDisabled() =
         testScope.runTest {
-            utils.mobileConnectionsRepository.isAnySimSecure.value = true
+            kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
             val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
 
             prepareState(
@@ -658,7 +670,7 @@
             )
             underTest.start()
             runCurrent()
-            utils.mobileConnectionsRepository.isAnySimSecure.value = false
+            kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
             runCurrent()
 
             assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
@@ -730,8 +742,8 @@
             }
         }
         sceneContainerFlags.enabled = true
-        utils.deviceEntryRepository.setUnlocked(isDeviceUnlocked)
-        utils.deviceEntryRepository.setBypassEnabled(isBypassEnabled)
+        kosmos.fakeDeviceEntryRepository.setUnlocked(isDeviceUnlocked)
+        kosmos.fakeDeviceEntryRepository.setBypassEnabled(isBypassEnabled)
         val transitionStateFlow =
             MutableStateFlow<ObservableTransitionState>(
                 ObservableTransitionState.Idle(SceneKey.Lockscreen)
@@ -743,8 +755,8 @@
             sceneInteractor.onSceneChanged(SceneModel(it), "reason")
         }
         authenticationMethod?.let {
-            utils.authenticationRepository.setAuthenticationMethod(authenticationMethod)
-            utils.deviceEntryRepository.setLockscreenEnabled(
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authenticationMethod)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(
                 isLockscreenEnabled = isLockscreenEnabled
             )
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 3a4ee64..ede453d8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -21,10 +21,14 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.sceneKeys
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
@@ -36,17 +40,17 @@
 @RunWith(AndroidJUnit4::class)
 class SceneContainerViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val interactor = utils.sceneInteractor()
+    private val kosmos = testKosmos()
+    private val interactor = kosmos.sceneInteractor
     private lateinit var underTest: SceneContainerViewModel
 
     @Before
     fun setUp() {
-        utils.fakeSceneContainerFlags.enabled = true
+        kosmos.fakeSceneContainerFlags.enabled = true
         underTest =
             SceneContainerViewModel(
                 sceneInteractor = interactor,
-                falsingInteractor = utils.falsingInteractor(),
+                falsingInteractor = kosmos.falsingInteractor,
             )
     }
 
@@ -64,7 +68,7 @@
 
     @Test
     fun allSceneKeys() {
-        assertThat(underTest.allSceneKeys).isEqualTo(utils.fakeSceneKeys())
+        assertThat(underTest.allSceneKeys).isEqualTo(kosmos.sceneKeys)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index 77ddf15..5174502 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -7,7 +7,8 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.ObservableTransitionState
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -18,6 +19,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -31,9 +33,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ShadeHeaderViewModelTest : SysuiTestCase() {
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
 
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
     private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index a8133a3a..bf873c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -19,16 +19,21 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -36,6 +41,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -53,10 +59,10 @@
 @RunWith(AndroidJUnit4::class)
 class ShadeSceneViewModelTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val sceneInteractor = utils.sceneInteractor()
-    private val deviceEntryInteractor = utils.deviceEntryInteractor()
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val deviceEntryInteractor = kosmos.deviceEntryInteractor
 
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
     private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
@@ -105,7 +111,7 @@
                 deviceEntryInteractor = deviceEntryInteractor,
                 shadeHeaderViewModel = shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
-                notifications = utils.notificationsPlaceholderViewModel(),
+                notifications = kosmos.notificationsPlaceholderViewModel,
                 mediaDataManager = mediaDataManager,
                 mediaHost = mediaHost,
             )
@@ -115,8 +121,10 @@
     fun upTransitionSceneKey_deviceLocked_lockScreen() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
 
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen)
         }
@@ -125,8 +133,10 @@
     fun upTransitionSceneKey_deviceUnlocked_gone() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
 
             assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone)
         }
@@ -135,8 +145,10 @@
     fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
             sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
 
@@ -147,8 +159,10 @@
     fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
         testScope.runTest {
             val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey)
-            utils.deviceEntryRepository.setLockscreenEnabled(true)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
             sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
             sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
 
@@ -159,8 +173,10 @@
     fun onContentClicked_deviceUnlocked_switchesToGone() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
             runCurrent()
 
             underTest.onContentClicked()
@@ -172,8 +188,10 @@
     fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-            utils.deviceEntryRepository.setUnlocked(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryRepository.setUnlocked(false)
             runCurrent()
 
             underTest.onContentClicked()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
similarity index 69%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
index 0a10b2c..0c7ce97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt
@@ -16,11 +16,10 @@
 
 package com.android.systemui.statusbar.notification.collection.render
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -34,18 +33,19 @@
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
+import org.junit.runner.RunWith
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
+@RunWith(AndroidJUnit4::class)
 class GroupExpansionManagerTest : SysuiTestCase() {
-    private lateinit var gem: GroupExpansionManagerImpl
+    private lateinit var underTest: GroupExpansionManagerImpl
 
     private val dumpManager: DumpManager = mock()
     private val groupMembershipManager: GroupMembershipManager = mock()
-    private val featureFlags = FakeFeatureFlagsClassic()
 
     private val pipeline: NotifPipeline = mock()
     private lateinit var beforeRenderListListener: OnBeforeRenderListListener
@@ -85,79 +85,57 @@
         whenever(groupMembershipManager.getGroupSummary(summary1)).thenReturn(summary1)
         whenever(groupMembershipManager.getGroupSummary(summary2)).thenReturn(summary2)
 
-        gem = GroupExpansionManagerImpl(dumpManager, groupMembershipManager, featureFlags)
+        underTest = GroupExpansionManagerImpl(dumpManager, groupMembershipManager)
     }
 
     @Test
-    fun testNotifyOnlyOnChange_enabled() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
+    fun notifyOnlyOnChange() {
         var listenerCalledCount = 0
-        gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
+        underTest.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
 
-        gem.setGroupExpanded(summary1, false)
+        underTest.setGroupExpanded(summary1, false)
         assertThat(listenerCalledCount).isEqualTo(0)
-        gem.setGroupExpanded(summary1, true)
+        underTest.setGroupExpanded(summary1, true)
         assertThat(listenerCalledCount).isEqualTo(1)
-        gem.setGroupExpanded(summary2, true)
+        underTest.setGroupExpanded(summary2, true)
         assertThat(listenerCalledCount).isEqualTo(2)
-        gem.setGroupExpanded(summary1, true)
+        underTest.setGroupExpanded(summary1, true)
         assertThat(listenerCalledCount).isEqualTo(2)
-        gem.setGroupExpanded(summary2, false)
+        underTest.setGroupExpanded(summary2, false)
         assertThat(listenerCalledCount).isEqualTo(3)
     }
 
     @Test
-    fun testNotifyOnlyOnChange_disabled() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
-
-        var listenerCalledCount = 0
-        gem.registerGroupExpansionChangeListener { _, _ -> listenerCalledCount++ }
-
-        gem.setGroupExpanded(summary1, false)
-        assertThat(listenerCalledCount).isEqualTo(1)
-        gem.setGroupExpanded(summary1, true)
-        assertThat(listenerCalledCount).isEqualTo(2)
-        gem.setGroupExpanded(summary2, true)
-        assertThat(listenerCalledCount).isEqualTo(3)
-        gem.setGroupExpanded(summary1, true)
-        assertThat(listenerCalledCount).isEqualTo(4)
-        gem.setGroupExpanded(summary2, false)
-        assertThat(listenerCalledCount).isEqualTo(5)
-    }
-
-    @Test
-    fun testExpandUnattachedEntry() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
+    fun expandUnattachedEntry() {
         // First, expand the entry when it is attached.
-        gem.setGroupExpanded(summary1, true)
-        assertThat(gem.isGroupExpanded(summary1)).isTrue()
+        underTest.setGroupExpanded(summary1, true)
+        assertThat(underTest.isGroupExpanded(summary1)).isTrue()
 
         // Un-attach it, and un-expand it.
         NotificationEntryBuilder.setNewParent(summary1, null)
-        gem.setGroupExpanded(summary1, false)
+        underTest.setGroupExpanded(summary1, false)
 
         // Expanding again should throw.
-        assertThrows(IllegalArgumentException::class.java) { gem.setGroupExpanded(summary1, true) }
+        assertThrows(IllegalArgumentException::class.java) {
+            underTest.setGroupExpanded(summary1, true)
+        }
     }
 
     @Test
-    fun testSyncWithPipeline() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-        gem.attach(pipeline)
+    fun syncWithPipeline() {
+        underTest.attach(pipeline)
         beforeRenderListListener = withArgCaptor {
             verify(pipeline).addOnBeforeRenderListListener(capture())
         }
 
         val listener: OnGroupExpansionChangeListener = mock()
-        gem.registerGroupExpansionChangeListener(listener)
+        underTest.registerGroupExpansionChangeListener(listener)
 
         beforeRenderListListener.onBeforeRenderList(entries)
         verify(listener, never()).onGroupExpansionChange(any(), any())
 
         // Expand one of the groups.
-        gem.setGroupExpanded(summary1, true)
+        underTest.setGroupExpanded(summary1, true)
         verify(listener).onGroupExpansionChange(summary1.row, true)
 
         // Empty the pipeline list and verify that the group is no longer expanded.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
new file mode 100644
index 0000000..2cbcc5a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.statusbar.notification.collection.render
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GroupMembershipManagerTest : SysuiTestCase() {
+    private var underTest = GroupMembershipManagerImpl()
+
+    @Test
+    fun isChildInGroup_topLevel() {
+        val topLevelEntry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+        assertThat(underTest.isChildInGroup(topLevelEntry)).isFalse()
+    }
+
+    @Test
+    fun isChildInGroup_noParent() {
+        val noParentEntry = NotificationEntryBuilder().setParent(null).build()
+        assertThat(underTest.isChildInGroup(noParentEntry)).isFalse()
+    }
+
+    @Test
+    fun isChildInGroup_summary() {
+        val groupKey = "group"
+        val summary =
+            NotificationEntryBuilder()
+                .setGroup(mContext, groupKey)
+                .setGroupSummary(mContext, true)
+                .build()
+        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+        assertThat(underTest.isChildInGroup(summary)).isFalse()
+    }
+
+    @Test
+    fun isGroupSummary_topLevelEntry() {
+        val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+        assertThat(underTest.isGroupSummary(entry)).isFalse()
+    }
+
+    @Test
+    fun isGroupSummary_summary() {
+        val groupKey = "group"
+        val summary =
+            NotificationEntryBuilder()
+                .setGroup(mContext, groupKey)
+                .setGroupSummary(mContext, true)
+                .build()
+        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+        assertThat(underTest.isGroupSummary(summary)).isTrue()
+    }
+
+    @Test
+    fun isGroupSummary_child() {
+        val groupKey = "group"
+        val summary =
+            NotificationEntryBuilder()
+                .setGroup(mContext, groupKey)
+                .setGroupSummary(mContext, true)
+                .build()
+        val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+        GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+        assertThat(underTest.isGroupSummary(entry)).isFalse()
+    }
+
+    @Test
+    fun getGroupSummary_topLevelEntry() {
+        val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
+        assertThat(underTest.getGroupSummary(entry)).isNull()
+    }
+
+    @Test
+    fun getGroupSummary_summary() {
+        val groupKey = "group"
+        val summary =
+            NotificationEntryBuilder()
+                .setGroup(mContext, groupKey)
+                .setGroupSummary(mContext, true)
+                .build()
+        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
+
+        assertThat(underTest.getGroupSummary(summary)).isEqualTo(summary)
+    }
+
+    @Test
+    fun getGroupSummary_child() {
+        val groupKey = "group"
+        val summary =
+            NotificationEntryBuilder()
+                .setGroup(mContext, groupKey)
+                .setGroupSummary(mContext, true)
+                .build()
+        val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
+        GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
+
+        assertThat(underTest.getGroupSummary(entry)).isEqualTo(summary)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
index 262795f..8e8e510 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryImplTest.kt
@@ -24,12 +24,13 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.telephony.TelephonyListenerManager
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -39,7 +40,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class TelephonyRepositoryImplTest : SysuiTestCase() {
@@ -47,8 +47,8 @@
     @Mock private lateinit var manager: TelephonyListenerManager
     @Mock private lateinit var telecomManager: TelecomManager
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
 
     private lateinit var underTest: TelephonyRepositoryImpl
 
@@ -61,7 +61,7 @@
             TelephonyRepositoryImpl(
                 applicationScope = testScope.backgroundScope,
                 applicationContext = context,
-                backgroundDispatcher = utils.testDispatcher,
+                backgroundDispatcher = kosmos.testDispatcher,
                 manager = manager,
                 telecomManager = telecomManager,
             )
diff --git a/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml b/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml
new file mode 100644
index 0000000..84b89ca
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/bouncer_password_text_view_focused_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="16dp" />
+            <!--By default this outline will not show hence 0 width.
+             width is set programmatically when needed and is gated by the flag:
+             com.android.systemui.Flags.pinInputFieldStyledFocusState-->
+            <stroke android:width="0dp"
+                android:color="@color/bouncer_password_focus_color" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
index 66c54f2..0b35559 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_message_area.xml
@@ -23,7 +23,7 @@
         android:layout_marginTop="@dimen/keyguard_lock_padding"
         android:importantForAccessibility="no"
         android:ellipsize="marquee"
-        android:focusable="true"
+        android:focusable="false"
         android:gravity="center"
         android:singleLine="true" />
 </merge>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 0628c3e..ddad1e3 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -25,6 +25,12 @@
     <!-- Maximum width of the sliding KeyguardSecurityContainer -->
     <dimen name="keyguard_security_width">420dp</dimen>
 
+    <!-- Width for the keyguard pin input field -->
+    <dimen name="keyguard_pin_field_width">292dp</dimen>
+
+    <!-- Width for the keyguard pin input field -->
+    <dimen name="keyguard_pin_field_height">48dp</dimen>
+
     <!-- Height of the sliding KeyguardSecurityContainer
          (includes 2x keyguard_security_view_top_margin) -->
     <dimen name="keyguard_security_height">420dp</dimen>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 565ed10..f51e109 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -62,10 +62,10 @@
     <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
 
     <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that charging is temporarily limited.  -->
-    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging optimized to protect battery</string>
+    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging on hold to protect battery</string>
 
     <!-- When the lock screen is showing and the phone plugged in with incompatible charger. -->
-    <string name="keyguard_plugged_in_incompatible_charger"><xliff:g id="percentage">%s</xliff:g> • Issue with charging accessory</string>
+    <string name="keyguard_plugged_in_incompatible_charger"><xliff:g id="percentage">%s</xliff:g> • Check charging accessory</string>
 
     <!-- SIM messages --><skip />
     <!-- When the user inserts a sim card from an unsupported network, it becomes network locked -->
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 2cca951..4789a22 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -76,6 +76,7 @@
     </style>
     <style name="Widget.TextView.Password" parent="@android:style/Widget.TextView">
         <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:background">@drawable/bouncer_password_text_view_focused_background</item>
         <item name="android:gravity">center</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
index 23fbb12..10f7113 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
@@ -20,6 +20,13 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
+    <ImageView
+        android:id="@+id/logo"
+        android:layout_width="@dimen/biometric_auth_icon_size"
+        android:layout_height="@dimen/biometric_auth_icon_size"
+        android:layout_gravity="center"
+        android:scaleType="fitXY"/>
+
     <TextView
         android:id="@+id/title"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 85b6e8d..5db9eee 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -50,7 +50,10 @@
         app:layout_constraintStart_toStartOf="@id/album_art"
         app:layout_constraintEnd_toEndOf="@id/album_art"
         app:layout_constraintTop_toTopOf="@id/album_art"
-        app:layout_constraintBottom_toBottomOf="@id/album_art" />
+        app:layout_constraintBottom_toBottomOf="@id/album_art"
+        android:clipToOutline="true"
+        android:background="@drawable/qs_media_outline_layout_bg"
+        />
 
     <com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
         android:id="@+id/turbulence_noise_view"
@@ -59,7 +62,10 @@
         app:layout_constraintStart_toStartOf="@id/album_art"
         app:layout_constraintEnd_toEndOf="@id/album_art"
         app:layout_constraintTop_toTopOf="@id/album_art"
-        app:layout_constraintBottom_toBottomOf="@id/album_art" />
+        app:layout_constraintBottom_toBottomOf="@id/album_art"
+        android:clipToOutline="true"
+        android:background="@drawable/qs_media_outline_layout_bg"
+        />
 
     <!-- Guideline for output switcher -->
     <androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index bcc3c83..61a323d4 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -93,6 +93,8 @@
     <color name="qs_user_switcher_selected_avatar_icon_color">#202124</color>
     <!-- Color of background circle of user avatars in quick settings user switcher -->
     <color name="qs_user_switcher_avatar_background">#3C4043</color>
+    <!-- Color of border for keyguard password input when focused -->
+    <color name="bouncer_password_focus_color">@*android:color/system_secondary_dark</color>
 
     <!-- Accessibility floating menu -->
     <color name="accessibility_floating_menu_background">#B3000000</color> <!-- 70% -->
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8be1cc7..3839dd9 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -56,6 +56,8 @@
     <color name="kg_user_switcher_restricted_avatar_icon_color">@color/GM2_grey_600</color>
     <!-- Color of background circle of user avatars in keyguard user switcher -->
     <color name="user_avatar_color_bg">?android:attr/colorBackgroundFloating</color>
+    <!-- Color of border for keyguard password input when focused -->
+    <color name="bouncer_password_focus_color">@*android:color/system_secondary_light</color>
 
     <!-- Icon color for user avatars in user switcher quick settings -->
     <color name="qs_user_switcher_avatar_icon_color">#3C4043</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 798fc06b..ee2a1ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -897,6 +897,10 @@
     <dimen name="communal_tutorial_indicator_padding">24dp</dimen>
     <dimen name="communal_tutorial_indicator_horizontal_offset">32dp</dimen>
 
+    <!-- Size of the maximum radius for the enforced rounded rectangles on communal hub.
+        Keep it the same as in Launcher-->
+    <dimen name="communal_enforced_rounded_corner_max_radius">16dp</dimen>
+
     <!-- The width/height of the unlock icon view on keyguard. -->
     <dimen name="keyguard_lock_height">42dp</dimen>
     <dimen name="keyguard_lock_padding">20dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 6e035e8..ec4c7d5 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -249,6 +249,9 @@
     -->
     <item type="id" name="tag_smartspace_view" />
 
+    <!-- ID of the Scene Container root Composable view -->
+    <item type='id' name="scene_container_root_composable" />
+
     <!-- Tag set on the Compose implementation of the QS footer actions. -->
     <item type="id" name="tag_compose_qs_footer_actions" />
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 66f965a..efd8f7f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -37,6 +37,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyboard.data.repository.KeyboardRepository;
 import com.android.systemui.log.BouncerLogger;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -210,6 +211,7 @@
         private final FeatureFlags mFeatureFlags;
         private final SelectedUserInteractor mSelectedUserInteractor;
         private final UiEventLogger mUiEventLogger;
+        private final KeyboardRepository mKeyboardRepository;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -223,7 +225,8 @@
                 DevicePostureController devicePostureController,
                 KeyguardViewController keyguardViewController,
                 FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
-                UiEventLogger uiEventLogger) {
+                UiEventLogger uiEventLogger,
+                KeyboardRepository keyboardRepository) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -240,6 +243,7 @@
             mFeatureFlags = featureFlags;
             mSelectedUserInteractor = selectedUserInteractor;
             mUiEventLogger = uiEventLogger;
+            mKeyboardRepository = keyboardRepository;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -268,19 +272,22 @@
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
                         mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
-                        mUiEventLogger);
+                        mUiEventLogger, mKeyboardRepository
+                );
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
-                        emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
+                        emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
+                        mKeyboardRepository);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
-                        emergencyButtonController, mFeatureFlags, mSelectedUserInteractor);
+                        emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
+                        mKeyboardRepository);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 36fe75f..fcff0db 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -25,6 +25,7 @@
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
 import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -168,7 +169,9 @@
 
         // Set selected property on so the view can send accessibility events.
         mPasswordEntry.setSelected(true);
-        mPasswordEntry.setDefaultFocusHighlightEnabled(false);
+        if (!pinInputFieldStyledFocusState()) {
+            mPasswordEntry.setDefaultFocusHighlightEnabled(false);
+        }
 
         mOkButton = findViewById(R.id.key_enter);
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 376933d..60dd568 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,17 +16,25 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
+
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnKeyListener;
 import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
 
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyboard.data.repository.KeyboardRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -35,6 +43,7 @@
 
     private final LiftToActivateListener mLiftToActivateListener;
     private final FalsingCollector mFalsingCollector;
+    private final KeyboardRepository mKeyboardRepository;
     protected PasswordTextView mPasswordEntry;
 
     private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
@@ -65,12 +74,14 @@
             EmergencyButtonController emergencyButtonController,
             FalsingCollector falsingCollector,
             FeatureFlags featureFlags,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            KeyboardRepository keyboardRepository) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
                 emergencyButtonController, featureFlags, selectedUserInteractor);
         mLiftToActivateListener = liftToActivateListener;
         mFalsingCollector = falsingCollector;
+        mKeyboardRepository = keyboardRepository;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
     }
 
@@ -120,6 +131,35 @@
             });
             okButton.setOnHoverListener(mLiftToActivateListener);
         }
+        if (pinInputFieldStyledFocusState()) {
+            collectFlow(mPasswordEntry, mKeyboardRepository.isAnyKeyboardConnected(),
+                    this::setKeyboardBasedFocusOutline);
+
+            /**
+             * new UI Specs for PIN Input field have new dimensions go/pin-focus-states.
+             * However we want these changes behind a flag, and resource files cannot be flagged
+             * hence the dimension change in code. When the flags are removed these dimensions
+             * should be set in resources permanently and the code below removed.
+             */
+            ViewGroup.LayoutParams layoutParams = mPasswordEntry.getLayoutParams();
+            layoutParams.width = (int) getResources().getDimension(
+                    R.dimen.keyguard_pin_field_width);
+            layoutParams.height = (int) getResources().getDimension(
+                    R.dimen.keyguard_pin_field_height);
+        }
+    }
+
+    private void setKeyboardBasedFocusOutline(boolean isAnyKeyboardConnected) {
+        StateListDrawable background = (StateListDrawable) mPasswordEntry.getBackground();
+        GradientDrawable stateDrawable = (GradientDrawable) background.getStateDrawable(0);
+        int color = getResources().getColor(R.color.bouncer_password_focus_color);
+        if (!isAnyKeyboardConnected) {
+            stateDrawable.setStroke(0, color);
+        } else {
+            int strokeWidthInDP = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3,
+                    getResources().getDisplayMetrics());
+            stateDrawable.setStroke(strokeWidthInDP, color);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 2aab1f1..b958f55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -28,6 +28,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.keyboard.data.repository.KeyboardRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -58,12 +59,13 @@
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             EmergencyButtonController emergencyButtonController,
             FalsingCollector falsingCollector,
-            DevicePostureController postureController,
-            FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
-            UiEventLogger uiEventLogger) {
+            DevicePostureController postureController, FeatureFlags featureFlags,
+            SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
+            KeyboardRepository keyboardRepository) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
+                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
+                keyboardRepository);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPostureController = postureController;
         mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 5729119..1cdcbd0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -44,6 +44,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyboard.data.repository.KeyboardRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -93,10 +94,11 @@
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor, KeyboardRepository keyboardRepository) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
+                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
+                keyboardRepository);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 05fb5fa..f019d61 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -39,6 +39,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyboard.data.repository.KeyboardRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
@@ -90,10 +91,11 @@
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor, KeyboardRepository keyboardRepository) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
-                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
+                emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
+                keyboardRepository);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
index b15aaaf..e88aaf01 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
@@ -91,7 +91,7 @@
         return app
     }
 
-    @UsesReflection(KeepTarget(extendsClassConstant = SysUIComponent::class, methodName = "inject"))
+    @UsesReflection(KeepTarget(instanceOfClassConstant = SysUIComponent::class, methodName = "inject"))
     override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider {
         val contentProvider = super.instantiateProviderCompat(cl, className)
         if (contentProvider is ContextInitializer) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt
index 6483ae4..c7e5b64 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepository.kt
@@ -19,16 +19,20 @@
 import android.os.UserHandle
 import android.provider.Settings.Secure
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.withContext
 
 /** Provides data related to color correction. */
@@ -45,22 +49,24 @@
 @Inject
 constructor(
     @Background private val bgCoroutineContext: CoroutineContext,
+    @Application private val scope: CoroutineScope,
     private val secureSettings: SecureSettings,
 ) : ColorCorrectionRepository {
 
-    companion object {
-        const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
-        const val DISABLED = 0
-        const val ENABLED = 1
-    }
+    private val userMap = mutableMapOf<Int, Flow<Boolean>>()
 
     override fun isEnabled(userHandle: UserHandle): Flow<Boolean> =
-        secureSettings
-            .observerFlow(userHandle.identifier, SETTING_NAME)
-            .onStart { emit(Unit) }
-            .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED }
-            .distinctUntilChanged()
-            .flowOn(bgCoroutineContext)
+        userMap.getOrPut(userHandle.identifier) {
+            secureSettings
+                .observerFlow(userHandle.identifier, SETTING_NAME)
+                .onStart { emit(Unit) }
+                .map {
+                    secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED
+                }
+                .distinctUntilChanged()
+                .flowOn(bgCoroutineContext)
+                .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
+        }
 
     override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean =
         withContext(bgCoroutineContext) {
@@ -70,4 +76,10 @@
                 userHandle.identifier
             )
         }
+
+    companion object {
+        private const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED
+        private const val DISABLED = 0
+        private const val ENABLED = 1
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt
index bbf10c5..419eada 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt
@@ -19,16 +19,20 @@
 import android.os.UserHandle
 import android.provider.Settings.Secure
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.withContext
 
 /** Provides data related to color inversion. */
@@ -45,16 +49,24 @@
 @Inject
 constructor(
     @Background private val bgCoroutineContext: CoroutineContext,
+    @Application private val scope: CoroutineScope,
     private val secureSettings: SecureSettings,
 ) : ColorInversionRepository {
 
+    private val userMap = mutableMapOf<Int, Flow<Boolean>>()
+
     override fun isEnabled(userHandle: UserHandle): Flow<Boolean> =
-        secureSettings
-            .observerFlow(userHandle.identifier, SETTING_NAME)
-            .onStart { emit(Unit) }
-            .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED }
-            .distinctUntilChanged()
-            .flowOn(bgCoroutineContext)
+        userMap.getOrPut(userHandle.identifier) {
+            secureSettings
+                .observerFlow(userHandle.identifier, SETTING_NAME)
+                .onStart { emit(Unit) }
+                .map {
+                    secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED
+                }
+                .distinctUntilChanged()
+                .flowOn(bgCoroutineContext)
+                .shareIn(scope, SharingStarted.WhileSubscribed(), replay = 1)
+        }
 
     override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean =
         withContext(bgCoroutineContext) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index ab23564..57e308f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -399,7 +399,8 @@
                     config.mPromptInfo,
                     config.mUserId,
                     config.mOperationId,
-                    new BiometricModalities(fpProps, faceProps));
+                    new BiometricModalities(fpProps, faceProps),
+                    config.mOpPackageName);
 
             final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
                     R.layout.biometric_prompt_layout, null, false);
@@ -470,7 +471,8 @@
         mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
 
         mPromptSelectorInteractorProvider.get().useCredentialsForAuthentication(
-                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId);
+                mConfig.mPromptInfo, credentialType, mConfig.mUserId, mConfig.mOperationId,
+                mConfig.mOpPackageName);
         final CredentialViewModel vm = mCredentialViewModelProvider.get();
         vm.setAnimateContents(animateContents);
         ((CredentialView) mCredentialView).init(vm, this, mPanelController, animatePanel);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
index 86802a5b..02eae9ced 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
@@ -148,6 +148,12 @@
                     || child.getId() == R.id.customized_view_container) {
                 //skip description view and compute later
                 continue;
+            } else if (child.getId() == R.id.logo) {
+                child.measure(
+                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
+                                MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
+                                MeasureSpec.EXACTLY));
             } else {
                 child.measure(
                         MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index b35fbbc..ad7bb0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -55,6 +55,9 @@
     /** The kind of credential to use (biometric, pin, pattern, etc.). */
     val kind: StateFlow<PromptKind>
 
+    /** The package name that the prompt is called from. */
+    val opPackageName: StateFlow<String?>
+
     /**
      * If explicit confirmation is required.
      *
@@ -68,6 +71,7 @@
         userId: Int,
         gatekeeperChallenge: Long?,
         kind: PromptKind,
+        opPackageName: String,
     )
 
     /** Unset the prompt info. */
@@ -108,6 +112,9 @@
     private val _kind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.Biometric())
     override val kind = _kind.asStateFlow()
 
+    private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
+    override val opPackageName = _opPackageName.asStateFlow()
+
     private val _faceSettings =
         _userId.map { id -> faceSettings.forUser(id) }.distinctUntilChanged()
     private val _faceSettingAlwaysRequireConfirmation =
@@ -127,11 +134,13 @@
         userId: Int,
         gatekeeperChallenge: Long?,
         kind: PromptKind,
+        opPackageName: String,
     ) {
         _kind.value = kind
         _userId.value = userId
         _challenge.value = gatekeeperChallenge
         _promptInfo.value = promptInfo
+        _opPackageName.value = opPackageName
     }
 
     override fun unsetPrompt() {
@@ -139,6 +148,7 @@
         _userId.value = null
         _challenge.value = null
         _kind.value = PromptKind.Biometric()
+        _opPackageName.value = null
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index ac4b717..359e2e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -115,12 +115,14 @@
         @Utils.CredentialType kind: Int,
         userId: Int,
         challenge: Long,
+        opPackageName: String,
     ) {
         biometricPromptRepository.setPrompt(
             promptInfo,
             userId,
             challenge,
-            kind.asBiometricPromptCredential()
+            kind.asBiometricPromptCredential(),
+            opPackageName,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index 65a2c0a..b3f9574 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -76,6 +76,7 @@
         userId: Int,
         challenge: Long,
         modalities: BiometricModalities,
+        opPackageName: String,
     )
 
     /** Use credential-based authentication instead of biometrics. */
@@ -84,6 +85,7 @@
         @Utils.CredentialType kind: Int,
         userId: Int,
         challenge: Long,
+        opPackageName: String,
     )
 
     /** Unset the current authentication request. */
@@ -104,9 +106,12 @@
             promptRepository.promptInfo,
             promptRepository.challenge,
             promptRepository.userId,
-            promptRepository.kind
-        ) { promptInfo, challenge, userId, kind ->
-            if (promptInfo == null || userId == null || challenge == null) {
+            promptRepository.kind,
+            promptRepository.opPackageName,
+        ) { promptInfo, challenge, userId, kind, opPackageName ->
+            if (
+                promptInfo == null || userId == null || challenge == null || opPackageName == null
+            ) {
                 return@combine null
             }
 
@@ -117,6 +122,7 @@
                         userInfo = BiometricUserInfo(userId = userId),
                         operationInfo = BiometricOperationInfo(gatekeeperChallenge = challenge),
                         modalities = kind.activeModalities,
+                        opPackageName = opPackageName,
                     )
                 else -> null
             }
@@ -152,13 +158,15 @@
         promptInfo: PromptInfo,
         userId: Int,
         challenge: Long,
-        modalities: BiometricModalities
+        modalities: BiometricModalities,
+        opPackageName: String,
     ) {
         promptRepository.setPrompt(
             promptInfo = promptInfo,
             userId = userId,
             gatekeeperChallenge = challenge,
             kind = PromptKind.Biometric(modalities),
+            opPackageName = opPackageName,
         )
     }
 
@@ -167,12 +175,14 @@
         @Utils.CredentialType kind: Int,
         userId: Int,
         challenge: Long,
+        opPackageName: String,
     ) {
         promptRepository.setPrompt(
             promptInfo = promptInfo,
             userId = userId,
             gatekeeperChallenge = challenge,
             kind = kind.asBiometricPromptCredential(),
+            opPackageName = opPackageName,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 4377937..c17c8dc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.biometrics.domain.model
 
+import android.graphics.Bitmap
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptInfo
 import com.android.systemui.biometrics.shared.model.BiometricModalities
@@ -26,6 +27,7 @@
         userInfo: BiometricUserInfo,
         operationInfo: BiometricOperationInfo,
         val modalities: BiometricModalities,
+        val opPackageName: String,
     ) :
         BiometricPromptRequest(
             title = info.title?.toString() ?: "",
@@ -36,6 +38,8 @@
             showEmergencyCallButton = info.isShowEmergencyCallButton
         ) {
         val contentView: PromptContentView? = info.contentView
+        val logoRes: Int = info.logoRes
+        val logoBitmap: Bitmap? = info.logoBitmap
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
index 60b454e..b450896 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
@@ -115,6 +115,12 @@
                                 MeasureSpec.EXACTLY),
                         MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height,
                                 MeasureSpec.EXACTLY));
+            } else if (child.getId() == R.id.logo) {
+                child.measure(
+                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
+                                MeasureSpec.EXACTLY),
+                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
+                                MeasureSpec.EXACTLY));
             } else if (child.getId() == R.id.biometric_icon) {
                 child.measure(
                         MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index 22b02da..16e7f05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -20,9 +20,9 @@
 import android.content.res.Resources
 import android.content.res.Resources.Theme
 import android.graphics.Paint
-import android.hardware.biometrics.PromptContentListItem
-import android.hardware.biometrics.PromptContentListItemBulletedText
-import android.hardware.biometrics.PromptContentListItemPlainText
+import android.hardware.biometrics.PromptContentItem
+import android.hardware.biometrics.PromptContentItemBulletedText
+import android.hardware.biometrics.PromptContentItemPlainText
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptVerticalListContentView
 import android.text.SpannableString
@@ -111,21 +111,21 @@
     return inflater.inflate(R.layout.biometric_prompt_content_row_layout, null) as LinearLayout
 }
 
-private fun PromptContentListItem.doesExceedMaxLinesIfTwoColumn(
+private fun PromptContentItem.doesExceedMaxLinesIfTwoColumn(
     resources: Resources,
 ): Boolean {
     val passedInText: CharSequence =
         when (this) {
-            is PromptContentListItemPlainText -> text
-            is PromptContentListItemBulletedText -> text
+            is PromptContentItemPlainText -> text
+            is PromptContentItemBulletedText -> text
             else -> {
-                throw IllegalStateException("No such ListItem: $this")
+                throw IllegalStateException("No such PromptContentItem: $this")
             }
         }
 
     when (this) {
-        is PromptContentListItemPlainText,
-        is PromptContentListItemBulletedText -> {
+        is PromptContentItemPlainText,
+        is PromptContentItemBulletedText -> {
             val dialogMargin =
                 resources.getDimensionPixelSize(R.dimen.biometric_dialog_border_padding)
             val halfDialogWidth =
@@ -155,12 +155,12 @@
             return numLines > maxLines
         }
         else -> {
-            throw IllegalStateException("No such ListItem: $this")
+            throw IllegalStateException("No such PromptContentItem: $this")
         }
     }
 }
 
-private fun PromptContentListItem.toView(
+private fun PromptContentItem.toView(
     resources: Resources,
     inflater: LayoutInflater,
     theme: Theme,
@@ -171,10 +171,10 @@
     textView.layoutParams = lp
 
     when (this) {
-        is PromptContentListItemPlainText -> {
+        is PromptContentItemPlainText -> {
             textView.text = text
         }
-        is PromptContentListItemBulletedText -> {
+        is PromptContentItemBulletedText -> {
             val bulletedText = SpannableString(text)
             val span =
                 BulletSpan(
@@ -186,25 +186,25 @@
             textView.text = bulletedText
         }
         else -> {
-            throw IllegalStateException("No such ListItem: $this")
+            throw IllegalStateException("No such PromptContentItem: $this")
         }
     }
     return textView
 }
 
-private fun PromptContentListItem.getListItemPadding(resources: Resources): Int {
+private fun PromptContentItem.getListItemPadding(resources: Resources): Int {
     var listItemPadding =
         resources.getDimensionPixelSize(
             R.dimen.biometric_prompt_content_list_item_padding_horizontal
         ) * 2
     when (this) {
-        is PromptContentListItemPlainText -> {}
-        is PromptContentListItemBulletedText -> {
+        is PromptContentItemPlainText -> {}
+        is PromptContentItemBulletedText -> {
             listItemPadding +=
                 getListItemBulletRadius(resources) * 2 + getListItemBulletGapWidth(resources)
         }
         else -> {
-            throw IllegalStateException("No such ListItem: $this")
+            throw IllegalStateException("No such PromptContentItem: $this")
         }
     }
     return listItemPadding
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 04dc7a8..285ab4a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -31,6 +31,7 @@
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
 import android.view.accessibility.AccessibilityManager
 import android.widget.Button
+import android.widget.ImageView
 import android.widget.ScrollView
 import android.widget.TextView
 import androidx.lifecycle.DefaultLifecycleObserver
@@ -92,6 +93,7 @@
         val textColorHint =
             view.resources.getColor(R.color.biometric_dialog_gray, view.context.theme)
 
+        val logoView = view.requireViewById<ImageView>(R.id.logo)
         val titleView = view.requireViewById<TextView>(R.id.title)
         val subtitleView = view.requireViewById<TextView>(R.id.subtitle)
         val descriptionView = view.requireViewById<TextView>(R.id.description)
@@ -99,6 +101,8 @@
             view.requireViewById<ScrollView>(R.id.customized_view_container)
 
         // set selected to enable marquee unless a screen reader is enabled
+        logoView.isSelected =
+            !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
         titleView.isSelected =
             !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
         subtitleView.isSelected =
@@ -152,6 +156,7 @@
                 }
             }
 
+            logoView.setImageDrawable(viewModel.logo.first())
             titleView.text = viewModel.title.first()
             subtitleView.text = viewModel.subtitle.first()
             descriptionView.text = viewModel.description.first()
@@ -183,6 +188,7 @@
                     viewModel = viewModel,
                     viewsToHideWhenSmall =
                         listOf(
+                            logoView,
                             titleView,
                             subtitleView,
                             descriptionView,
@@ -190,6 +196,7 @@
                         ),
                     viewsToFadeInOnSizeChange =
                         listOf(
+                            logoView,
                             titleView,
                             subtitleView,
                             descriptionView,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index c3bbaed..d5695f3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -25,6 +25,7 @@
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.core.animation.addListener
 import androidx.core.view.doOnLayout
@@ -234,7 +235,13 @@
 
 private fun View.showContentOrHide(forceHide: Boolean = false) {
     val isTextViewWithBlankText = this is TextView && this.text.isBlank()
-    visibility = if (forceHide || isTextViewWithBlankText) View.GONE else View.VISIBLE
+    val isImageViewWithoutImage = this is ImageView && this.drawable == null
+    visibility =
+        if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+            View.GONE
+        } else {
+            View.VISIBLE
+        }
 }
 
 private fun View.asVerticalAnimator(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 1c78928..dca0338 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -18,6 +18,8 @@
 
 import android.content.Context
 import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
 import android.hardware.biometrics.BiometricPrompt
 import android.hardware.biometrics.PromptContentView
 import android.util.Log
@@ -233,6 +235,19 @@
             }
         }
 
+    /** Logo for the prompt. */
+    val logo: Flow<Drawable?> =
+        promptSelectorInteractor.prompt
+            .map {
+                when {
+                    it == null -> null
+                    it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
+                    it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
+                    else -> context.packageManager.getApplicationIcon(it.opPackageName)
+                }
+            }
+            .distinctUntilChanged()
+
     /** Title for the prompt. */
     val title: Flow<String> =
         promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 10768ea..dc07c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.communal.data.db.CommunalDatabaseModule
 import com.android.systemui.communal.data.repository.CommunalMediaRepositoryModule
+import com.android.systemui.communal.data.repository.CommunalPrefsRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalTutorialRepositoryModule
 import com.android.systemui.communal.data.repository.CommunalWidgetRepositoryModule
@@ -34,6 +35,7 @@
             CommunalTutorialRepositoryModule::class,
             CommunalWidgetRepositoryModule::class,
             CommunalDatabaseModule::class,
+            CommunalPrefsRepositoryModule::class,
         ]
 )
 interface CommunalModule {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
new file mode 100644
index 0000000..c2ea2e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.data.repository
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.UserInfo
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserFileManagerExt.observeSharedPreferences
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
+
+/**
+ * Stores simple preferences for the current user in communal hub. For use cases like "has the CTA
+ * tile been dismissed?"
+ */
+interface CommunalPrefsRepository {
+
+    /** Whether the CTA tile has been dismissed. */
+    val isCtaDismissed: Flow<Boolean>
+
+    /** Save the CTA tile dismissed state for the current user. */
+    suspend fun setCtaDismissedForCurrentUser()
+}
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class CommunalPrefsRepositoryImpl
+@Inject
+constructor(
+    @Background private val backgroundScope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val userRepository: UserRepository,
+    private val userFileManager: UserFileManager,
+) : CommunalPrefsRepository {
+
+    override val isCtaDismissed: Flow<Boolean> =
+        userRepository.selectedUserInfo
+            .flatMapLatest(::observeCtaDismissState)
+            .stateIn(
+                scope = backgroundScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+
+    override suspend fun setCtaDismissedForCurrentUser() =
+        withContext(bgDispatcher) {
+            getSharedPrefsForUser(userRepository.getSelectedUserInfo())
+                .edit()
+                .putBoolean(CTA_DISMISSED_STATE, true)
+                .apply()
+        }
+
+    private fun observeCtaDismissState(user: UserInfo): Flow<Boolean> =
+        userFileManager
+            .observeSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
+            // Emit at the start of collection to ensure we get an initial value
+            .onStart { emit(Unit) }
+            .map { getCtaDismissedState() }
+            .flowOn(bgDispatcher)
+
+    private suspend fun getCtaDismissedState(): Boolean =
+        withContext(bgDispatcher) {
+            getSharedPrefsForUser(userRepository.getSelectedUserInfo())
+                .getBoolean(CTA_DISMISSED_STATE, false)
+        }
+
+    private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
+        return userFileManager.getSharedPreferences(
+            FILE_NAME,
+            Context.MODE_PRIVATE,
+            user.id,
+        )
+    }
+
+    companion object {
+        const val TAG = "CommunalRepository"
+        const val FILE_NAME = "communal_hub_prefs"
+        const val CTA_DISMISSED_STATE = "cta_dismissed"
+    }
+}
diff --git a/core/java/android/hardware/biometrics/PromptContentListItem.java b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryModule.kt
similarity index 60%
copy from core/java/android/hardware/biometrics/PromptContentListItem.java
copy to packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryModule.kt
index fa3783d..a4ff6d3 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItem.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryModule.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,18 +12,15 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ *
  */
 
-package android.hardware.biometrics;
+package com.android.systemui.communal.data.repository
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import dagger.Binds
+import dagger.Module
 
-import android.annotation.FlaggedApi;
-
-/**
- * A list item shown on {@link PromptVerticalListContentView}.
- */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public interface PromptContentListItem {
+@Module
+interface CommunalPrefsRepositoryModule {
+    @Binds fun communalPrefsRepository(impl: CommunalPrefsRepositoryImpl): CommunalPrefsRepository
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 553b3eb..1f4be40 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -56,9 +56,6 @@
     /** Exposes the transition state of the communal [SceneTransitionLayout]. */
     val transitionState: StateFlow<ObservableCommunalTransitionState>
 
-    /** Whether the CTA tile is visible in the hub under view mode. */
-    val isCtaTileInViewModeVisible: Flow<Boolean>
-
     /** Updates the requested scene. */
     fun setDesiredScene(desiredScene: CommunalSceneKey)
 
@@ -68,9 +65,6 @@
      * Note that you must call is with `null` when the UI is done or risk a memory leak.
      */
     fun setTransitionState(transitionState: Flow<ObservableCommunalTransitionState>?)
-
-    /** Updates whether to display the CTA tile in the hub under view mode. */
-    fun setCtaTileInViewModeVisibility(isVisible: Boolean)
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -102,16 +96,6 @@
                 initialValue = defaultTransitionState,
             )
 
-    // TODO(b/313462210) - persist the value in local storage, so the tile won't show up again
-    //  once dismissed.
-    private val _isCtaTileInViewModeVisible: MutableStateFlow<Boolean> = MutableStateFlow(true)
-    override val isCtaTileInViewModeVisible: Flow<Boolean> =
-        _isCtaTileInViewModeVisible.asStateFlow()
-
-    override fun setCtaTileInViewModeVisibility(isVisible: Boolean) {
-        _isCtaTileInViewModeVisible.value = isVisible
-    }
-
     override fun setDesiredScene(desiredScene: CommunalSceneKey) {
         _desiredScene.value = desiredScene
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index e6816e9..bfc5019 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.communal.data.repository
 
-import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
 import android.content.ComponentName
 import android.content.Intent
@@ -29,6 +28,7 @@
 import com.android.systemui.communal.data.db.CommunalWidgetItem
 import com.android.systemui.communal.shared.CommunalWidgetHost
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -83,7 +83,7 @@
 @Inject
 constructor(
     private val appWidgetManager: Optional<AppWidgetManager>,
-    private val appWidgetHost: AppWidgetHost,
+    private val appWidgetHost: CommunalAppWidgetHost,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
     broadcastDispatcher: BroadcastDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index d0d9e3f..52f42c1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -17,11 +17,11 @@
 
 package com.android.systemui.communal.data.repository
 
-import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
 import android.content.Context
 import android.content.res.Resources
 import com.android.systemui.communal.shared.CommunalWidgetHost
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
@@ -48,15 +48,15 @@
 
         @SysUISingleton
         @Provides
-        fun provideAppWidgetHost(@Application context: Context): AppWidgetHost {
-            return AppWidgetHost(context, APP_WIDGET_HOST_ID)
+        fun provideCommunalAppWidgetHost(@Application context: Context): CommunalAppWidgetHost {
+            return CommunalAppWidgetHost(context, APP_WIDGET_HOST_ID)
         }
 
         @SysUISingleton
         @Provides
         fun provideCommunalWidgetHost(
             appWidgetManager: Optional<AppWidgetManager>,
-            appWidgetHost: AppWidgetHost,
+            appWidgetHost: CommunalAppWidgetHost,
             @CommunalLog logBuffer: LogBuffer,
         ): CommunalWidgetHost {
             return CommunalWidgetHost(appWidgetManager, appWidgetHost, logBuffer)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 9fa4cd6..aa4a9d0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.communal.domain.interactor
 
 import android.app.smartspace.SmartspaceTarget
-import android.appwidget.AppWidgetHost
 import android.content.ComponentName
 import com.android.systemui.communal.data.repository.CommunalMediaRepository
+import com.android.systemui.communal.data.repository.CommunalPrefsRepository
 import com.android.systemui.communal.data.repository.CommunalRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -29,6 +29,7 @@
 import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -49,10 +50,11 @@
 constructor(
     private val communalRepository: CommunalRepository,
     private val widgetRepository: CommunalWidgetRepository,
+    private val communalPrefsRepository: CommunalPrefsRepository,
     mediaRepository: CommunalMediaRepository,
     smartspaceRepository: SmartspaceRepository,
     keyguardInteractor: KeyguardInteractor,
-    private val appWidgetHost: AppWidgetHost,
+    private val appWidgetHost: CommunalAppWidgetHost,
     private val editWidgetsActivityStarter: EditWidgetsActivityStarter
 ) {
 
@@ -122,7 +124,7 @@
     }
 
     /** Dismiss the CTA tile from the hub in view mode. */
-    fun dismissCtaTile() = communalRepository.setCtaTileInViewModeVisibility(isVisible = false)
+    suspend fun dismissCtaTile() = communalPrefsRepository.setCtaDismissedForCurrentUser()
 
     /**
      * Add a widget at the specified position.
@@ -174,8 +176,8 @@
 
     /** CTA tile to be displayed in the glanceable hub (view mode). */
     val ctaTileContent: Flow<List<CommunalContentModel.CtaTileInViewMode>> =
-        communalRepository.isCtaTileInViewModeVisible.map { visible ->
-            if (visible) listOf(CommunalContentModel.CtaTileInViewMode()) else emptyList()
+        communalPrefsRepository.isCtaDismissed.map { isDismissed ->
+            if (isDismissed) emptyList() else listOf(CommunalContentModel.CtaTileInViewMode())
         }
 
     /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 46f957f..0d52afd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.communal.domain.model
 
-import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetProviderInfo
 import android.widget.RemoteViews
 import com.android.systemui.communal.shared.model.CommunalContentSize
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import java.util.UUID
 
 /** Encapsulates data for a communal content. */
@@ -44,7 +44,7 @@
     class Widget(
         val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
-        val appWidgetHost: AppWidgetHost,
+        val appWidgetHost: CommunalAppWidgetHost,
     ) : CommunalContentModel {
         override val key = KEY.widget(appWidgetId)
         // Widget size is always half.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
index 41f9cb4..7fe37cc 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/CommunalWidgetHost.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.communal.shared
 
-import android.appwidget.AppWidgetHost
 import android.appwidget.AppWidgetManager
 import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_CONFIGURATION_OPTIONAL
 import android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE
 import android.content.ComponentName
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
@@ -35,7 +35,7 @@
 @Inject
 constructor(
     private val appWidgetManager: Optional<AppWidgetManager>,
-    private val appWidgetHost: AppWidgetHost,
+    private val appWidgetHost: CommunalAppWidgetHost,
     @CommunalLog logBuffer: LogBuffer,
 ) {
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index fcad45f..317dd40 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -20,7 +20,6 @@
 import android.app.Activity.RESULT_CANCELED
 import android.app.Activity.RESULT_OK
 import android.app.ActivityOptions
-import android.appwidget.AppWidgetHost
 import android.content.ActivityNotFoundException
 import android.content.ComponentName
 import android.widget.RemoteViews
@@ -28,6 +27,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.controls.ui.MediaHost
 import com.android.systemui.media.dagger.MediaModule
@@ -48,7 +48,7 @@
 @Inject
 constructor(
     private val communalInteractor: CommunalInteractor,
-    private val appWidgetHost: AppWidgetHost,
+    private val appWidgetHost: CommunalAppWidgetHost,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     private val uiEventLogger: UiEventLogger,
 ) : BaseCommunalViewModel(communalInteractor, mediaHost) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 09c18ed..d619362 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -86,9 +86,11 @@
     override fun onOpenWidgetEditor() = communalInteractor.showWidgetEditor()
 
     override fun onDismissCtaTile() {
-        communalInteractor.dismissCtaTile()
-        setPopupOnDismissCtaVisibility(true)
-        schedulePopupHiding()
+        scope.launch {
+            communalInteractor.dismissCtaTile()
+            setPopupOnDismissCtaVisibility(true)
+            schedulePopupHiding()
+        }
     }
 
     override fun getInteractionHandler(): RemoteViews.InteractionHandler = interactionHandler
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
new file mode 100644
index 0000000..003c9d5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.widgets
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Context
+
+/** Communal app widget host that creates a [CommunalAppWidgetHostView]. */
+class CommunalAppWidgetHost(context: Context, hostId: Int) : AppWidgetHost(context, hostId) {
+    override fun onCreateView(
+        context: Context,
+        appWidgetId: Int,
+        appWidget: AppWidgetProviderInfo?
+    ): AppWidgetHostView {
+        return CommunalAppWidgetHostView(context)
+    }
+
+    /**
+     * Creates and returns a [CommunalAppWidgetHostView]. This method does the same thing as
+     * `createView`. The only difference is that the returned value will be casted to
+     * [CommunalAppWidgetHostView].
+     */
+    fun createViewForCommunal(
+        context: Context?,
+        appWidgetId: Int,
+        appWidget: AppWidgetProviderInfo?
+    ): CommunalAppWidgetHostView {
+        // `createView` internally calls `onCreateView` to create the view. We cannot override
+        // `createView`, but we are sure that the hostView is `CommunalAppWidgetHostView`
+        return createView(context, appWidgetId, appWidget) as CommunalAppWidgetHostView
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
new file mode 100644
index 0000000..2b7d823
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.widgets
+
+import android.appwidget.AppWidgetHostView
+import android.content.Context
+import android.graphics.Outline
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewOutlineProvider
+
+/** AppWidgetHostView that displays in communal hub with support for rounded corners. */
+class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context) {
+    // Mutable corner radius.
+    var enforcedCornerRadius: Float
+
+    // Mutable `Rect`. The size will be mutated when the widget is reapplied.
+    var enforcedRectangle: Rect
+
+    init {
+        enforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context)
+        enforcedRectangle = Rect()
+    }
+
+    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+        super.onLayout(changed, l, t, r, b)
+
+        enforceRoundedCorners()
+    }
+
+    private val cornerRadiusEnforcementOutline: ViewOutlineProvider =
+        object : ViewOutlineProvider() {
+            override fun getOutline(view: View?, outline: Outline) {
+                if (enforcedRectangle.isEmpty || enforcedCornerRadius <= 0) {
+                    outline.setEmpty()
+                } else {
+                    outline.setRoundRect(enforcedRectangle, enforcedCornerRadius)
+                }
+            }
+        }
+
+    private fun enforceRoundedCorners() {
+        if (enforcedCornerRadius <= 0) {
+            resetRoundedCorners()
+            return
+        }
+        val background: View? = RoundedCornerEnforcement.findBackground(this)
+        if (background == null || RoundedCornerEnforcement.hasAppWidgetOptedOut(this, background)) {
+            resetRoundedCorners()
+            return
+        }
+        RoundedCornerEnforcement.computeRoundedRectangle(this, background, enforcedRectangle)
+        outlineProvider = cornerRadiusEnforcementOutline
+        clipToOutline = true
+        invalidateOutline()
+    }
+
+    private fun resetRoundedCorners() {
+        outlineProvider = ViewOutlineProvider.BACKGROUND
+        clipToOutline = false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt
new file mode 100644
index 0000000..abda44b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/RoundedCornerEnforcement.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.widgets
+
+import android.annotation.IdRes
+import android.annotation.Nullable
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.os.BuildCompat.isAtLeastS
+import com.android.systemui.res.R
+import kotlin.math.min
+
+/**
+ * Utilities to compute the enforced use of rounded corners on App Widgets. This is a fork of the
+ * Launcher3 source code to enforce the same visual treatment on communal hub.
+ */
+internal object RoundedCornerEnforcement {
+    /**
+     * Find the background view for a widget.
+     *
+     * @param appWidget the view containing the App Widget (typically the instance of
+     *   [CommunalAppWidgetHostView]).
+     */
+    fun findBackground(appWidget: View): View? {
+        val backgrounds = findViewsWithId(appWidget, R.id.background)
+        if (backgrounds.size == 1) {
+            return backgrounds[0]
+        }
+        // Really, the argument should contain the widget, so it cannot be the background.
+        if (appWidget is ViewGroup) {
+            val vg = appWidget
+            if (vg.childCount > 0) {
+                return findUndefinedBackground(vg.getChildAt(0))
+            }
+        }
+        return appWidget
+    }
+
+    /** Check whether the app widget has opted out of the enforcement. */
+    fun hasAppWidgetOptedOut(appWidget: View?, background: View): Boolean {
+        return background.id == R.id.background && background.clipToOutline
+    }
+
+    /**
+     * Computes the rounded rectangle needed for this app widget.
+     *
+     * @param appWidget View onto which the rounded rectangle will be applied.
+     * @param background Background view. This must be either `appWidget` or a descendant of
+     *   `appWidget`.
+     * @param outRect Rectangle set to the rounded rectangle coordinates, in the reference frame of
+     *   `appWidget`.
+     */
+    fun computeRoundedRectangle(appWidget: View, background: View, outRect: Rect) {
+        var background = background
+        outRect.left = 0
+        outRect.right = background.width
+        outRect.top = 0
+        outRect.bottom = background.height
+        while (background !== appWidget) {
+            outRect.offset(background.left, background.top)
+            background = background.parent as View
+        }
+    }
+
+    /** Get the radius of the rounded rectangle defined in the host's resource. */
+    private fun getOwnedEnforcedRadius(context: Context): Float {
+        val res: Resources = context.resources
+        return res.getDimension(R.dimen.communal_enforced_rounded_corner_max_radius)
+    }
+
+    /**
+     * Computes the radius of the rounded rectangle that should be applied to a widget expanded in
+     * the given context.
+     */
+    fun computeEnforcedRadius(context: Context): Float {
+        if (!isAtLeastS()) {
+            return 0f
+        }
+        val res: Resources = context.resources
+        val systemRadius: Float =
+            res.getDimension(android.R.dimen.system_app_widget_background_radius)
+        val defaultRadius = getOwnedEnforcedRadius(context)
+        return min(defaultRadius, systemRadius)
+    }
+
+    private fun findViewsWithId(view: View, @IdRes viewId: Int): List<View> {
+        val output: MutableList<View> = ArrayList()
+        accumulateViewsWithId(view, viewId, output)
+        return output
+    }
+
+    // Traverse views. If the predicate returns true, continue on the children, otherwise, don't.
+    private fun accumulateViewsWithId(view: View, @IdRes viewId: Int, output: MutableList<View>) {
+        if (view.id == viewId) {
+            output.add(view)
+            return
+        }
+        if (view is ViewGroup) {
+            val vg = view
+            for (i in 0 until vg.childCount) {
+                accumulateViewsWithId(vg.getChildAt(i), viewId, output)
+            }
+        }
+    }
+
+    private fun isViewVisible(view: View): Boolean {
+        return if (view.visibility != View.VISIBLE) {
+            false
+        } else !view.willNotDraw() || view.foreground != null || view.background != null
+    }
+
+    @Nullable
+    private fun findUndefinedBackground(current: View): View? {
+        if (current.visibility != View.VISIBLE) {
+            return null
+        }
+        if (isViewVisible(current)) {
+            return current
+        }
+        var lastVisibleView: View? = null
+        // Find the first view that is either not a ViewGroup, or a ViewGroup which will draw
+        // something, or a ViewGroup that contains more than one view.
+        if (current is ViewGroup) {
+            val vg = current
+            for (i in 0 until vg.childCount) {
+                val visibleView = findUndefinedBackground(vg.getChildAt(i))
+                if (visibleView != null) {
+                    if (lastVisibleView != null) {
+                        return current // At least two visible children
+                    }
+                    lastVisibleView = visibleView
+                }
+            }
+        }
+        return lastVisibleView
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b2d7052..6d9994f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -556,7 +556,7 @@
     @Provides
     @Singleton
     static SubscriptionManager provideSubscriptionManager(Context context) {
-        return context.getSystemService(SubscriptionManager.class);
+        return context.getSystemService(SubscriptionManager.class).createForAllUserProfiles();
     }
 
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 846736c..ea8ba10 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -102,12 +102,6 @@
             default = true
         )
 
-    /** Only notify group expansion listeners when a change happens. */
-    // TODO(b/292213543): Tracking Bug
-    @JvmField
-    val NOTIFICATION_GROUP_EXPANSION_CHANGE =
-            releasedFlag("notification_group_expansion_change")
-
     // TODO(b/301955929)
     @JvmField
     val NOTIF_LS_BACKGROUND_THREAD =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
index 496c64e..c6fb4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
@@ -19,6 +19,8 @@
 
 import com.android.systemui.keyboard.data.repository.KeyboardRepository
 import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl
+import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository
+import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl
 import dagger.Binds
 import dagger.Module
 
@@ -27,4 +29,9 @@
 
     @Binds
     abstract fun bindKeyboardRepository(repository: KeyboardRepositoryImpl): KeyboardRepository
+
+    @Binds
+    abstract fun bindStickyKeysRepository(
+        repository: StickyKeysRepositoryImpl
+    ): StickyKeysRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt
new file mode 100644
index 0000000..37034f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/StickyKeysLogger.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyboard.stickykeys
+
+import com.android.systemui.keyboard.stickykeys.shared.model.Locked
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.KeyboardLog
+import javax.inject.Inject
+
+private const val TAG = "stickyKeys"
+
+class StickyKeysLogger @Inject constructor(@KeyboardLog private val buffer: LogBuffer) {
+    fun logNewStickyKeysReceived(linkedHashMap: Map<ModifierKey, Locked>) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            { str1 = linkedHashMap.toString() },
+            { "new sticky keys state received: $str1" }
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
new file mode 100644
index 0000000..34d2888
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/data/repository/StickyKeysRepository.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyboard.stickykeys.data.repository
+
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.StickyModifierStateListener
+import android.hardware.input.StickyModifierState
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
+import com.android.systemui.keyboard.stickykeys.shared.model.Locked
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import javax.inject.Inject
+
+interface StickyKeysRepository {
+    val stickyKeys: Flow<LinkedHashMap<ModifierKey, Locked>>
+    val settingEnabled: Flow<Boolean>
+}
+
+class StickyKeysRepositoryImpl
+@Inject
+constructor(
+    private val inputManager: InputManager,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val stickyKeysLogger: StickyKeysLogger,
+) : StickyKeysRepository {
+
+    override val stickyKeys: Flow<LinkedHashMap<ModifierKey, Locked>> =
+        conflatedCallbackFlow {
+                val listener = StickyModifierStateListener { stickyModifierState ->
+                    trySendWithFailureLogging(stickyModifierState, TAG)
+                }
+                // after registering, InputManager calls listener with the current value
+                inputManager.registerStickyModifierStateListener(Runnable::run, listener)
+                awaitClose { inputManager.unregisterStickyModifierStateListener(listener) }
+            }
+            .map { toStickyKeysMap(it) }
+            .onEach { stickyKeysLogger.logNewStickyKeysReceived(it) }
+            .flowOn(backgroundDispatcher)
+
+    // TODO(b/319837892): Implement reading actual setting
+    override val settingEnabled: StateFlow<Boolean> = MutableStateFlow(true)
+
+    private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> {
+        val keys = linkedMapOf<ModifierKey, Locked>()
+        state.apply {
+            if (isAltGrModifierOn) keys[ALT_GR] = Locked(false)
+            if (isAltGrModifierLocked) keys[ALT_GR] = Locked(true)
+            if (isAltModifierOn) keys[ALT] = Locked(false)
+            if (isAltModifierLocked) keys[ALT] = Locked(true)
+            if (isCtrlModifierOn) keys[CTRL] = Locked(false)
+            if (isCtrlModifierLocked) keys[CTRL] = Locked(true)
+            if (isMetaModifierOn) keys[META] = Locked(false)
+            if (isMetaModifierLocked) keys[META] = Locked(true)
+            if (isShiftModifierOn) keys[SHIFT] = Locked(false)
+            if (isShiftModifierLocked) keys[SHIFT] = Locked(true)
+        }
+        return keys
+    }
+
+    companion object {
+        const val TAG = "StickyKeysRepositoryImpl"
+    }
+}
diff --git a/core/java/android/hardware/biometrics/PromptContentListItem.java b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
similarity index 60%
copy from core/java/android/hardware/biometrics/PromptContentListItem.java
copy to packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
index fa3783d..d5f082a 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItem.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open 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,16 +14,15 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics;
+package com.android.systemui.keyboard.stickykeys.shared.model
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+@JvmInline
+value class Locked(val locked: Boolean)
 
-import android.annotation.FlaggedApi;
-
-/**
- * A list item shown on {@link PromptVerticalListContentView}.
- */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public interface PromptContentListItem {
+enum class ModifierKey(val text: String) {
+    ALT("ALT LEFT"),
+    ALT_GR("ALT RIGHT"),
+    CTRL("CTRL"),
+    META("META"),
+    SHIFT("SHIFT"),
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt
new file mode 100644
index 0000000..26eb706
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModel.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyboard.stickykeys.ui.viewmodel
+
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository
+import com.android.systemui.keyboard.stickykeys.shared.model.Locked
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
+
+class StickyKeysIndicatorViewModel
+@Inject
+constructor(
+    stickyKeysRepository: StickyKeysRepository,
+    keyboardRepository: KeyboardRepository,
+    @Application applicationScope: CoroutineScope,
+) {
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val indicatorContent: Flow<Map<ModifierKey, Locked>> =
+        keyboardRepository.isAnyKeyboardConnected
+            .flatMapLatest { keyboardPresent ->
+                if (keyboardPresent) stickyKeysRepository.settingEnabled else flowOf(false)
+            }
+            .flatMapLatest { enabled ->
+                if (enabled) stickyKeysRepository.stickyKeys else flowOf(emptyMap())
+            }
+            .stateIn(applicationScope, SharingStarted.Lazily, emptyMap())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index fedd63b..8fa33ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -54,12 +54,33 @@
     ) {
 
     override fun start() {
-        listenForAodToLockscreenOrOccluded()
+        listenForAodToLockscreen()
+        listenForAodToPrimaryBouncer()
         listenForAodToGone()
+        listenForAodToOccluded()
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
-    private fun listenForAodToLockscreenOrOccluded() {
+    /**
+     * There are cases where the transition to AOD begins but never completes, such as tapping power
+     * during an incoming phone call when unlocked. In this case, GONE->AOD should be interrupted to
+     * run AOD->OCCLUDED.
+     */
+    private fun listenForAodToOccluded() {
+        scope.launch {
+            keyguardInteractor.isKeyguardOccluded.sample(startedKeyguardState, ::Pair).collect {
+                (isOccluded, startedKeyguardState) ->
+                if (isOccluded && startedKeyguardState == KeyguardState.AOD) {
+                    startTransitionTo(
+                        toState = KeyguardState.OCCLUDED,
+                        modeOnCanceled = TransitionModeOnCanceled.RESET
+                    )
+                }
+            }
+        }
+    }
+
+    private fun listenForAodToLockscreen() {
         scope.launch {
             keyguardInteractor
                 .dozeTransitionTo(DozeStateModel.FINISH)
@@ -72,20 +93,15 @@
                     ::toTriple
                 )
                 .collect { (_, lastStartedStep, occluded) ->
-                    if (lastStartedStep.to == KeyguardState.AOD) {
-                        val toState =
-                            if (occluded) KeyguardState.OCCLUDED else KeyguardState.LOCKSCREEN
+                    if (lastStartedStep.to == KeyguardState.AOD && !occluded) {
                         val modeOnCanceled =
-                            if (
-                                toState == KeyguardState.LOCKSCREEN &&
-                                    lastStartedStep.from == KeyguardState.LOCKSCREEN
-                            ) {
+                            if (lastStartedStep.from == KeyguardState.LOCKSCREEN) {
                                 TransitionModeOnCanceled.REVERSE
                             } else {
                                 TransitionModeOnCanceled.LAST_VALUE
                             }
                         startTransitionTo(
-                            toState = toState,
+                            toState = KeyguardState.LOCKSCREEN,
                             modeOnCanceled = modeOnCanceled,
                         )
                     }
@@ -93,11 +109,26 @@
         }
     }
 
+    /**
+     * If there is a biometric lockout and FPS is tapped while on AOD, it should go directly to the
+     * PRIMARY_BOUNCER.
+     */
+    private fun listenForAodToPrimaryBouncer() {
+        scope.launch {
+            keyguardInteractor.primaryBouncerShowing
+                .sample(startedKeyguardTransitionStep, ::Pair)
+                .collect { (isBouncerShowing, lastStartedTransitionStep) ->
+                    if (isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.AOD) {
+                        startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+                    }
+                }
+        }
+    }
+
     private fun listenForAodToGone() {
         scope.launch {
             keyguardInteractor.biometricUnlockState.sample(finishedKeyguardState, ::Pair).collect {
-                pair ->
-                val (biometricUnlockState, keyguardState) = pair
+                (biometricUnlockState, keyguardState) ->
                 if (keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)) {
                     startTransitionTo(KeyguardState.GONE)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index 2a68f26..0c0eb8a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -24,6 +24,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.LEFT
 import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
 import androidx.constraintlayout.widget.ConstraintSet.RIGHT
+import androidx.constraintlayout.widget.ConstraintSet.VISIBILITY_MODE_IGNORE
 import com.android.systemui.Flags.keyguardBottomAreaRefactor
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
@@ -96,6 +97,11 @@
             constrainHeight(R.id.end_button, height)
             connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT, horizontalOffsetMargin)
             connect(R.id.end_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
+
+            // The constraint set visibility for start and end button are default visible, set to
+            // ignore so the view's own initial visibility (invisible) is used
+            setVisibilityMode(R.id.start_button, VISIBILITY_MODE_IGNORE)
+            setVisibilityMode(R.id.end_button, VISIBILITY_MODE_IGNORE)
         }
     }
 }
diff --git a/core/java/android/hardware/biometrics/PromptContentListItem.java b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyboardLog.kt
similarity index 60%
copy from core/java/android/hardware/biometrics/PromptContentListItem.java
copy to packages/SystemUI/src/com/android/systemui/log/dagger/KeyboardLog.kt
index fa3783d..5910701 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItem.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyboardLog.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open 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,16 +14,12 @@
  * limitations under the License.
  */
 
-package android.hardware.biometrics;
+package com.android.systemui.log.dagger
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import javax.inject.Qualifier
 
-import android.annotation.FlaggedApi;
-
-/**
- * A list item shown on {@link PromptVerticalListContentView}.
- */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public interface PromptContentListItem {
-}
-
+/** A [com.android.systemui.log.LogBuffer] for keyboard-related functionality. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class KeyboardLog
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 24cb8ff..3e00940 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -603,6 +603,14 @@
         return factory.create("BluetoothTileDialogLog", 50);
     }
 
+    /** Provides a {@link LogBuffer} for the keyboard functionalities. */
+    @Provides
+    @SysUISingleton
+    @KeyboardLog
+    public static LogBuffer provideKeyboardLogBuffer(LogBufferFactory factory) {
+        return factory.create("KeyboardLog", 50);
+    }
+
     /** Provides a {@link LogBuffer} for {@link PackageChangeRepository} */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/LeftRightArrowPressedListener.kt b/packages/SystemUI/src/com/android/systemui/qs/LeftRightArrowPressedListener.kt
new file mode 100644
index 0000000..ca790e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/LeftRightArrowPressedListener.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.view.KeyEvent
+import android.view.View
+import androidx.core.util.Consumer
+
+/**
+ * Listens for left and right arrow keys pressed while focus is on the view.
+ *
+ * Key press is treated as correct when its full lifecycle happened on the view: first
+ * [KeyEvent.ACTION_DOWN] was performed, view didn't lose focus in the meantime and then
+ * [KeyEvent.ACTION_UP] was performed with the same [KeyEvent.getKeyCode]
+ */
+class LeftRightArrowPressedListener private constructor() :
+    View.OnKeyListener, View.OnFocusChangeListener {
+
+    private var lastKeyCode: Int? = 0
+    private var listener: Consumer<Int>? = null
+
+    fun setArrowKeyPressedListener(arrowPressedListener: Consumer<Int>) {
+        listener = arrowPressedListener
+    }
+
+    override fun onKey(view: View, keyCode: Int, keyEvent: KeyEvent): Boolean {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
+            // only scroll on ACTION_UP as we don't handle longpressing for now. Still we need
+            // to intercept even ACTION_DOWN otherwise keyboard focus will be moved before we
+            // have a chance to intercept ACTION_UP.
+            if (keyEvent.action == KeyEvent.ACTION_UP && keyCode == lastKeyCode) {
+                listener?.accept(keyCode)
+                lastKeyCode = null
+            } else if (keyEvent.repeatCount == 0) {
+                // we only read key events that are NOT coming from long pressing because that also
+                // causes reading ACTION_DOWN event (with repeated count > 0) when moving focus with
+                // arrow from another sibling view
+                lastKeyCode = keyCode
+            }
+            return true
+        }
+        return false
+    }
+
+    override fun onFocusChange(view: View, hasFocus: Boolean) {
+        // resetting lastKeyCode so we get fresh cleared state on focus
+        if (hasFocus) {
+            lastKeyCode = null
+        }
+    }
+
+    companion object {
+        @JvmStatic
+        fun createAndRegisterListenerForView(view: View): LeftRightArrowPressedListener {
+            val listener = LeftRightArrowPressedListener()
+            view.setOnKeyListener(listener)
+            view.onFocusChangeListener = listener
+            return listener
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 4770d52..1c9f5fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -1,5 +1,8 @@
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.PageIndicator.PageScrollActionListener.LEFT;
+import static com.android.systemui.qs.PageIndicator.PageScrollActionListener.RIGHT;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -9,10 +12,12 @@
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.Utils;
@@ -43,6 +48,7 @@
 
     private int mPosition = -1;
     private boolean mAnimating;
+    private PageScrollActionListener mPageScrollActionListener;
 
     private final Animatable2.AnimationCallback mAnimationCallback =
             new Animatable2.AnimationCallback() {
@@ -77,6 +83,14 @@
         mPageIndicatorWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_width);
         mPageIndicatorHeight = res.getDimensionPixelSize(R.dimen.qs_page_indicator_height);
         mPageDotWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_dot_width);
+        LeftRightArrowPressedListener arrowListener =
+                LeftRightArrowPressedListener.createAndRegisterListenerForView(this);
+        arrowListener.setArrowKeyPressedListener(keyCode -> {
+            if (mPageScrollActionListener != null) {
+                int swipeDirection = keyCode == KeyEvent.KEYCODE_DPAD_LEFT ? LEFT : RIGHT;
+                mPageScrollActionListener.onScrollActionTriggered(swipeDirection);
+            }
+        });
     }
 
     public void setNumPages(int numPages) {
@@ -280,4 +294,19 @@
             getChildAt(i).layout(left, 0, mPageIndicatorWidth + left, mPageIndicatorHeight);
         }
     }
+
+    void setPageScrollActionListener(PageScrollActionListener listener) {
+        mPageScrollActionListener = listener;
+    }
+
+    interface PageScrollActionListener {
+
+        @IntDef({LEFT, RIGHT})
+        @interface Direction { }
+
+        int LEFT = 0;
+        int RIGHT = 1;
+
+        void onScrollActionTriggered(@Direction int swipeDirection);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 052c0da..43f3a22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,6 +1,8 @@
 package com.android.systemui.qs;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE;
+import static com.android.systemui.qs.PageIndicator.PageScrollActionListener.LEFT;
+import static com.android.systemui.qs.PageIndicator.PageScrollActionListener.RIGHT;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -12,7 +14,6 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.util.AttributeSet;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -30,6 +31,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.PageIndicator.PageScrollActionListener.Direction;
 import com.android.systemui.qs.QSPanel.QSTileLayout;
 import com.android.systemui.qs.QSPanelControllerBase.TileRecord;
 import com.android.systemui.qs.logging.QSLogger;
@@ -310,26 +312,18 @@
         mPageIndicator = indicator;
         mPageIndicator.setNumPages(mPages.size());
         mPageIndicator.setLocation(mPageIndicatorPosition);
-        mPageIndicator.setOnKeyListener((view, keyCode, keyEvent) -> {
-            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
-                // only scroll on ACTION_UP as we don't handle longpressing for now. Still we need
-                // to intercept even ACTION_DOWN otherwise keyboard focus will be moved before we
-                // have a chance to intercept ACTION_UP.
-                if (keyEvent.getAction() == KeyEvent.ACTION_UP && mScroller.isFinished()) {
-                    scrollByX(getDeltaXForKeyboardScrolling(keyCode),
-                            SINGLE_PAGE_SCROLL_DURATION_MILLIS);
-                }
-                return true;
+        mPageIndicator.setPageScrollActionListener(swipeDirection -> {
+            if (mScroller.isFinished()) {
+                scrollByX(getDeltaXForPageScrolling(swipeDirection),
+                        SINGLE_PAGE_SCROLL_DURATION_MILLIS);
             }
-            return false;
         });
     }
 
-    private int getDeltaXForKeyboardScrolling(int keyCode) {
-        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && getCurrentItem() != 0) {
+    private int getDeltaXForPageScrolling(@Direction int swipeDirection) {
+        if (swipeDirection == LEFT && getCurrentItem() != 0) {
             return -getWidth();
-        } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
-                && getCurrentItem() != mPages.size() - 1) {
+        } else if (swipeDirection == RIGHT && getCurrentItem() != mPages.size() - 1) {
             return getWidth();
         }
         return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index a6cccf1..e2959fe 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -24,7 +24,9 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
 import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.init.NotificationsController
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -44,6 +46,7 @@
     private val keyguardRepository: KeyguardRepository,
     private val headsUpManager: HeadsUpManager,
     private val powerInteractor: PowerInteractor,
+    private val activeNotificationsInteractor: ActiveNotificationsInteractor,
 ) : CoreStartable {
 
     private var notificationPresenter: NotificationPresenter? = null
@@ -117,6 +120,14 @@
         return if (headsUpManager.hasPinnedHeadsUp() && isNotifPresenterFullyCollapsed) {
             1
         } else {
+            getActiveNotificationsCount()
+        }
+    }
+
+    private fun getActiveNotificationsCount(): Int {
+        return if (NotificationsLiveDataStoreRefactor.isEnabled) {
+            activeNotificationsInteractor.allNotificationsCountValue
+        } else {
             notificationsController?.getActiveNotificationsCount() ?: 0
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index ab08f66..a755805 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -37,6 +37,7 @@
     /** The flag description -- not an aconfig flag name */
     const val DESCRIPTION = "SceneContainerFlag"
 
+    @JvmStatic
     inline val isEnabled
         get() =
             SCENE_CONTAINER_ENABLED && // mainStaticFlag
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 4a839b8..93cfc5d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -85,12 +85,13 @@
 
                     view.addView(
                         ComposeFacade.createSceneContainerView(
-                            scope = this,
-                            context = view.context,
-                            viewModel = viewModel,
-                            windowInsets = windowInsets,
-                            sceneByKey = sortedSceneByKey,
-                        )
+                                scope = this,
+                                context = view.context,
+                                viewModel = viewModel,
+                                windowInsets = windowInsets,
+                                sceneByKey = sortedSceneByKey,
+                            )
+                            .also { it.id = R.id.scene_container_root_composable }
                     )
 
                     val legacyView = view.requireViewById<View>(R.id.legacy_window_root)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt
new file mode 100644
index 0000000..b09bfe2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerExt.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings
+
+import android.annotation.UserIdInt
+import android.content.Context
+import android.content.SharedPreferences
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Extension functions for [UserFileManager]. */
+object UserFileManagerExt {
+
+    /** Returns a flow of [Unit] that is invoked each time the shared preference is updated. */
+    fun UserFileManager.observeSharedPreferences(
+        fileName: String,
+        @Context.PreferencesMode mode: Int,
+        @UserIdInt userId: Int
+    ): Flow<Unit> = conflatedCallbackFlow {
+        val sharedPrefs = getSharedPreferences(fileName, mode, userId)
+
+        val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> trySend(Unit) }
+
+        sharedPrefs.registerOnSharedPreferenceChangeListener(listener)
+        awaitClose { sharedPrefs.unregisterOnSharedPreferenceChangeListener(listener) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 2d5afd5..3cdb2cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -21,8 +21,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -53,14 +51,11 @@
      */
     private final Set<NotificationEntry> mExpandedGroups = new HashSet<>();
 
-    private final FeatureFlags mFeatureFlags;
-
     @Inject
     public GroupExpansionManagerImpl(DumpManager dumpManager,
-            GroupMembershipManager groupMembershipManager, FeatureFlags featureFlags) {
+            GroupMembershipManager groupMembershipManager) {
         mDumpManager = dumpManager;
         mGroupMembershipManager = groupMembershipManager;
-        mFeatureFlags = featureFlags;
     }
 
     /**
@@ -86,10 +81,8 @@
     };
 
     public void attach(NotifPipeline pipeline) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
-            mDumpManager.registerDumpable(this);
-            pipeline.addOnBeforeRenderListListener(mNotifTracker);
-        }
+        mDumpManager.registerDumpable(this);
+        pipeline.addOnBeforeRenderListListener(mNotifTracker);
     }
 
     @Override
@@ -105,8 +98,7 @@
     @Override
     public void setGroupExpanded(NotificationEntry entry, boolean expanded) {
         NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry);
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)
-                && entry.getParent() == null) {
+        if (entry.getParent() == null) {
             if (expanded) {
                 throw new IllegalArgumentException("Cannot expand group that is not attached");
             } else {
@@ -124,7 +116,7 @@
         }
 
         // Only notify listeners if something changed.
-        if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE) || changed) {
+        if (changed) {
             sendOnGroupExpandedChange(entry, expanded);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index cb79353..da12479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -22,8 +22,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -38,25 +36,17 @@
  */
 @SysUISingleton
 public class GroupMembershipManagerImpl implements GroupMembershipManager {
-    FeatureFlagsClassic mFeatureFlags;
-
     @Inject
-    public GroupMembershipManagerImpl(FeatureFlagsClassic featureFlags) {
-        mFeatureFlags = featureFlags;
-    }
+    public GroupMembershipManagerImpl() {}
 
     @Override
     public boolean isGroupSummary(@NonNull NotificationEntry entry) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
-            if (entry.getParent() == null) {
-                // The entry is not attached, so it doesn't count.
-                return false;
-            }
-            // If entry is a summary, its parent is a GroupEntry with summary = entry.
-            return entry.getParent().getSummary() == entry;
-        } else {
-            return getGroupSummary(entry) == entry;
+        if (entry.getParent() == null) {
+            // The entry is not attached, so it doesn't count.
+            return false;
         }
+        // If entry is a summary, its parent is a GroupEntry with summary = entry.
+        return entry.getParent().getSummary() == entry;
     }
 
     @Nullable
@@ -70,12 +60,8 @@
 
     @Override
     public boolean isChildInGroup(@NonNull NotificationEntry entry) {
-        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE)) {
-            // An entry is a child if it's not a summary or top level entry, but it is attached.
-            return !isGroupSummary(entry) && !isTopLevelEntry(entry) && entry.getParent() != null;
-        } else {
-            return !isTopLevelEntry(entry);
-        }
+        // An entry is a child if it's not a summary or top level entry, but it is attached.
+        return !isGroupSummary(entry) && !isTopLevelEntry(entry) && entry.getParent() != null;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
index 5180bab..b22e9fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt
@@ -68,7 +68,8 @@
     /**
      * The same as [allNotificationsCount], but without flows, for easy access in synchronous code.
      */
-    val allNotificationsCountValue: Int = repository.activeNotifications.value.individuals.size
+    val allNotificationsCountValue: Int
+        get() = repository.activeNotifications.value.individuals.size
 
     /** Are any notifications being actively presented in the notification stack? */
     val areAnyNotificationsPresent: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index ac49960..1677418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -18,7 +18,6 @@
 
 import android.service.notification.StatusBarNotification
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
 import com.android.systemui.statusbar.NotificationListener
@@ -72,7 +71,6 @@
     private val animatedImageNotificationManager: AnimatedImageNotificationManager,
     private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
     private val bubblesOptional: Optional<Bubbles>,
-    private val featureFlags: FeatureFlags
 ) : NotificationsController {
 
     override fun initialize(
@@ -136,5 +134,8 @@
         }
     }
 
-    override fun getActiveNotificationsCount(): Int = notifLiveDataStore.activeNotifCount.value
+    override fun getActiveNotificationsCount(): Int {
+        NotificationsLiveDataStoreRefactor.assertInLegacyMode()
+        return notifLiveDataStore.activeNotifCount.value
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index d9c5108..c527bb5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -26,9 +26,9 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.res.R;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e791a64..04db653 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -94,6 +94,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.TouchLogger;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -187,6 +188,7 @@
     private int mOverflingDistance;
     private float mMaxOverScroll;
     private boolean mIsBeingDragged;
+    private boolean mSendingTouchesToSceneFramework;
     private int mLastMotionY;
     private int mDownX;
     private int mActivePointerId = INVALID_POINTER;
@@ -1509,7 +1511,12 @@
      */
     public void setExpandedHeight(float height) {
         final boolean skipHeightUpdate = shouldSkipHeightUpdate();
-        updateStackPosition();
+
+        // when scene framework is enabled, updateStackPosition is already called by
+        // updateTopPadding every time the stack moves, so skip it here to avoid flickering.
+        if (!SceneContainerFlag.isEnabled()) {
+            updateStackPosition();
+        }
 
         if (!skipHeightUpdate) {
             mExpandedHeight = height;
@@ -2450,6 +2457,7 @@
                         /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
                         shelfIntrinsicHeight);
         mIntrinsicContentHeight = height;
+        mController.setIntrinsicContentHeight(mIntrinsicContentHeight);
 
         // The topPadding can be bigger than the regular padding when qs is expanded, in that
         // state the maxPanelHeight and the contentHeight should be bigger
@@ -3558,8 +3566,11 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mTouchHandler != null && mTouchHandler.onTouchEvent(ev)) {
-            return true;
+        if (mTouchHandler != null) {
+            boolean touchHandled = mTouchHandler.onTouchEvent(ev);
+            if (SceneContainerFlag.isEnabled() || touchHandled) {
+                return touchHandled;
+            }
         }
 
         return super.onTouchEvent(ev);
@@ -3567,6 +3578,27 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (SceneContainerFlag.isEnabled() && mIsBeingDragged) {
+            if (!mSendingTouchesToSceneFramework) {
+                // if this is the first touch being sent to the scene framework,
+                // convert it into a synthetic DOWN event.
+                mSendingTouchesToSceneFramework = true;
+                MotionEvent downEvent = MotionEvent.obtain(ev);
+                downEvent.setAction(MotionEvent.ACTION_DOWN);
+                mController.sendTouchToSceneFramework(downEvent);
+                downEvent.recycle();
+            } else {
+                mController.sendTouchToSceneFramework(ev);
+            }
+
+            if (
+                    ev.getActionMasked() == MotionEvent.ACTION_UP
+                    || ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+            ) {
+                setIsBeingDragged(false);
+            }
+            return false;
+        }
         return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
     }
 
@@ -3633,6 +3665,18 @@
             return true;
         }
 
+        // If the scene framework is enabled, ignore all non-move gestures if we are currently
+        // dragging - they should be dispatched to the scene framework. Move gestures should be let
+        // through to determine if we are still dragging or not.
+        if (
+                SceneContainerFlag.isEnabled()
+                && mIsBeingDragged
+                && action != MotionEvent.ACTION_MOVE
+        ) {
+            setIsBeingDragged(false);
+            return false;
+        }
+
         switch (action) {
             case MotionEvent.ACTION_DOWN: {
                 if (getChildCount() == 0 || !isInContentBounds(ev)) {
@@ -3676,6 +3720,11 @@
                     }
                 }
                 if (mIsBeingDragged) {
+                    // Defer actual scrolling to the scene framework if enabled
+                    if (SceneContainerFlag.isEnabled()) {
+                        setIsBeingDragged(false);
+                        return false;
+                    }
                     // Scroll to follow the motion event
                     mLastMotionY = y;
                     float scrollAmount;
@@ -3770,9 +3819,8 @@
     }
 
     protected boolean isInsideQsHeader(MotionEvent ev) {
-        if (mQsHeader == null) {
-            Log.wtf(TAG, "qsHeader is null while NSSL is handling a touch");
-            return false;
+        if (SceneContainerFlag.isEnabled()) {
+            return ev.getY() < mController.getPlaceholderTop();
         }
 
         mQsHeader.getBoundsOnScreen(mQsHeaderBound);
@@ -4026,9 +4074,16 @@
             requestDisallowInterceptTouchEvent(true);
             cancelLongPress();
             resetExposedMenuView(true /* animate */, true /* force */);
+        } else {
+            mSendingTouchesToSceneFramework = false;
         }
     }
 
+    @VisibleForTesting
+    boolean getIsBeingDragged() {
+        return mIsBeingDragged;
+    }
+
     public void requestDisallowLongPress() {
         cancelLongPress();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c0e0b73..6a66bb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -80,6 +80,9 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.ui.view.WindowRootView;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -120,6 +123,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -145,6 +149,7 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Provider;
 
 /**
  * Controller for {@link NotificationStackScrollLayout}.
@@ -181,6 +186,8 @@
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
     private final ShadeController mShadeController;
+    private final Provider<WindowRootView> mWindowRootView;
+    private final NotificationStackAppearanceInteractor mStackAppearanceInteractor;
     private final KeyguardMediaController mKeyguardMediaController;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardBypassController mKeyguardBypassController;
@@ -689,6 +696,9 @@
             SeenNotificationsInteractor seenNotificationsInteractor,
             NotificationListViewBinder viewBinder,
             ShadeController shadeController,
+            SceneContainerFlags sceneContainerFlags,
+            Provider<WindowRootView> windowRootView,
+            NotificationStackAppearanceInteractor stackAppearanceInteractor,
             InteractionJankMonitor jankMonitor,
             StackStateLogger stackLogger,
             NotificationStackScrollLogger logger,
@@ -739,6 +749,8 @@
         mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
         mSeenNotificationsInteractor = seenNotificationsInteractor;
         mShadeController = shadeController;
+        mWindowRootView = windowRootView;
+        mStackAppearanceInteractor = stackAppearanceInteractor;
         mFeatureFlags = featureFlags;
         mNotificationTargetsHelper = notificationTargetsHelper;
         mSecureSettings = secureSettings;
@@ -1076,6 +1088,28 @@
         return mView.getIntrinsicContentHeight();
     }
 
+    /**
+     * Dispatch a touch to the scene container framework.
+     * TODO(b/316965302): Replace findViewById to avoid DFS
+     */
+    public void sendTouchToSceneFramework(MotionEvent ev) {
+        View sceneContainer = mWindowRootView.get()
+                .findViewById(R.id.scene_container_root_composable);
+        if (sceneContainer != null) {
+            sceneContainer.dispatchTouchEvent(ev);
+        }
+    }
+
+    /** Get the y-coordinate of the top bound of the stack. */
+    public float getPlaceholderTop() {
+        return mStackAppearanceInteractor.getStackBounds().getValue().getTop();
+    }
+
+    /** Set the intrinsic height of the stack content without additional padding. */
+    public void setIntrinsicContentHeight(float intrinsicContentHeight) {
+        mStackAppearanceInteractor.setIntrinsicContentHeight(intrinsicContentHeight);
+    }
+
     public void setIntrinsicPadding(int intrinsicPadding) {
         mView.setIntrinsicPadding(intrinsicPadding);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
index e78a694..aac3c28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationStackAppearanceRepository.kt
@@ -30,4 +30,18 @@
 
     /** The corner radius of the notification stack, in dp. */
     val cornerRadiusDp = MutableStateFlow(32f)
+
+    /**
+     * The height in px of the contents of notification stack. Depending on the number of
+     * notifications, this can exceed the space available on screen to show notifications, at which
+     * point the notification stack should become scrollable.
+     */
+    val intrinsicContentHeight = MutableStateFlow(0f)
+
+    /**
+     * The y-coordinate in px of top of the contents of the notification stack. This value can be
+     * negative, if the stack is scrolled such that its top extends beyond the top edge of the
+     * screen.
+     */
+    val contentTop = MutableStateFlow(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 61a4dfc..1dfde09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -34,12 +34,32 @@
     /** The bounds of the notification stack in the current scene. */
     val stackBounds: StateFlow<NotificationContainerBounds> = repository.stackBounds.asStateFlow()
 
+    /** The corner radius of the notification stack, in dp. */
+    val cornerRadiusDp: StateFlow<Float> = repository.cornerRadiusDp.asStateFlow()
+
+    /**
+     * The height in px of the contents of notification stack. Depending on the number of
+     * notifications, this can exceed the space available on screen to show notifications, at which
+     * point the notification stack should become scrollable.
+     */
+    val intrinsicContentHeight = repository.intrinsicContentHeight.asStateFlow()
+
+    /** The y-coordinate in px of top of the contents of the notification stack. */
+    val contentTop = repository.contentTop.asStateFlow()
+
     /** Sets the position of the notification stack in the current scene. */
     fun setStackBounds(bounds: NotificationContainerBounds) {
         check(bounds.top <= bounds.bottom) { "Invalid bounds: $bounds" }
         repository.stackBounds.value = bounds
     }
 
-    /** The corner radius of the notification stack, in dp. */
-    val cornerRadiusDp: StateFlow<Float> = repository.cornerRadiusDp.asStateFlow()
+    /** Sets the height of the contents of the notification stack. */
+    fun setIntrinsicContentHeight(height: Float) {
+        repository.intrinsicContentHeight.value = height
+    }
+
+    /** Sets the y-coord in px of the top of the contents of the notification stack. */
+    fun setContentTop(startY: Float) {
+        repository.contentTop.value = startY
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
index a9b542d..ed15f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStackAppearanceViewBinder.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationStackAppearanceViewModel
+import kotlin.math.pow
 import kotlin.math.roundToInt
 import kotlinx.coroutines.launch
 
@@ -43,24 +44,28 @@
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
                     viewModel.stackBounds.collect { bounds ->
-                        controller.updateTopPadding(
-                            bounds.top,
-                            controller.isAddOrRemoveAnimationPending
-                        )
                         controller.setRoundedClippingBounds(
-                            it.left,
-                            it.top,
-                            it.right,
-                            it.bottom,
+                            bounds.left.roundToInt(),
+                            bounds.top.roundToInt(),
+                            bounds.right.roundToInt(),
+                            bounds.bottom.roundToInt(),
                             viewModel.cornerRadiusDp.value.dpToPx(context),
                             viewModel.cornerRadiusDp.value.dpToPx(context),
                         )
                     }
                 }
+
+                launch {
+                    viewModel.contentTop.collect {
+                        controller.updateTopPadding(it, controller.isAddOrRemoveAnimationPending)
+                    }
+                }
+
                 launch {
                     viewModel.expandFraction.collect { expandFraction ->
                         ambientState.expansionFraction = expandFraction
                         controller.expandedHeight = expandFraction * controller.view.height
+                        controller.setMaxAlphaForExpansion(expandFraction.pow(0.75f))
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
index 834d3ff..74db583 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationStackAppearanceViewModel.kt
@@ -41,4 +41,7 @@
 
     /** The corner radius of the notification stack, in dp. */
     val cornerRadiusDp: StateFlow<Float> = stackAppearanceInteractor.cornerRadiusDp
+
+    /** The y-coordinate in px of top of the contents of the notification stack. */
+    val contentTop: StateFlow<Float> = stackAppearanceInteractor.contentTop
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 9f22118..385f061 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -21,9 +21,11 @@
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.shared.flexiNotifsEnabled
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 
 /**
@@ -35,6 +37,7 @@
 @Inject
 constructor(
     private val interactor: NotificationStackAppearanceInteractor,
+    shadeInteractor: ShadeInteractor,
     flags: SceneContainerFlags,
     featureFlags: FeatureFlagsClassic,
 ) {
@@ -66,4 +69,22 @@
 
     /** The corner radius of the placeholder, in dp. */
     val cornerRadiusDp: StateFlow<Float> = interactor.cornerRadiusDp
+
+    /**
+     * The height in px of the contents of notification stack. Depending on the number of
+     * notifications, this can exceed the space available on screen to show notifications, at which
+     * point the notification stack should become scrollable.
+     */
+    val intrinsicContentHeight = interactor.intrinsicContentHeight
+
+    /**
+     * The amount [0-1] that the shade has been opened. At 0, the shade is closed; at 1, the shade
+     * is open.
+     */
+    val expandFraction: Flow<Float> = shadeInteractor.shadeExpansion
+
+    /** Sets the y-coord in px of the top of the contents of the notification stack. */
+    fun onContentTopChanged(padding: Float) {
+        interactor.setContentTop(padding)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index d8ef981..da6bfe8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -48,10 +48,10 @@
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -76,7 +76,7 @@
 
     protected MockitoSession mStaticMockSession;
 
-    protected final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
+    protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
     protected @Mock LockIconView mLockIconView;
     protected @Mock AnimatedStateListDrawable mIconDrawable;
@@ -175,7 +175,7 @@
                 mPrimaryBouncerInteractor,
                 mContext,
                 () -> mDeviceEntryInteractor,
-                mSceneTestUtils.getFakeSceneContainerFlags()
+                mKosmos.getFakeSceneContainerFlags()
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 132bdb5..b0887ef 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -373,7 +373,7 @@
     @Test
     public void longPress_showBouncer_sceneContainerNotEnabled() {
         init(/* useMigrationFlag= */ false);
-        mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(false);
+        mKosmos.getFakeSceneContainerFlags().setEnabled(false);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
 
         // WHEN longPress
@@ -387,7 +387,7 @@
     @Test
     public void longPress_showBouncer() {
         init(/* useMigrationFlag= */ false);
-        mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(true);
+        mKosmos.getFakeSceneContainerFlags().setEnabled(true);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
 
         // WHEN longPress
@@ -401,7 +401,7 @@
     @Test
     public void longPress_falsingTriggered_doesNotShowBouncer() {
         init(/* useMigrationFlag= */ false);
-        mSceneTestUtils.getFakeSceneContainerFlags().setEnabled(true);
+        mKosmos.getFakeSceneContainerFlags().setEnabled(true);
         when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
 
         // WHEN longPress
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 2afb3a1..d86d123 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -640,11 +640,10 @@
     @Test
     public void deleteWindowMagnification_enabling_expectedValuesAndInvokeCallback()
             throws RemoteException {
-
         enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
-        Mockito.reset(mSpyController);
+        resetMockObjects();
         getInstrumentation().runOnMainSync(() -> {
             mWindowMagnificationAnimationController.deleteWindowMagnification(
                     mAnimationCallback2);
@@ -658,6 +657,11 @@
             mValueAnimator.end();
         });
 
+        // wait for animation returns
+        waitForIdleSync();
+
+        // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} will only
+        // be triggered once in {@link ValueAnimator#end()}
         verify(mSpyController).enableWindowMagnificationInternal(
                 mScaleCaptor.capture(),
                 mCenterXCaptor.capture(), mCenterYCaptor.capture(),
@@ -717,7 +721,11 @@
         deleteWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
                 mAnimationCallback);
 
-        deleteWindowMagnificationAndWaitAnimating(0, null);
+        // Verifying that WindowMagnificationController#deleteWindowMagnification is never called
+        // in previous steps
+        verify(mSpyController, never()).deleteWindowMagnification();
+
+        deleteWindowMagnificationWithoutAnimation();
 
         verify(mSpyController).deleteWindowMagnification();
         verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
@@ -810,6 +818,8 @@
             mWindowMagnificationAnimationController.enableWindowMagnification(
                     targetScale, targetCenterX, targetCenterY, null);
         });
+        // wait for animation returns
+        waitForIdleSync();
     }
 
     private void enableWindowMagnificationAndWaitAnimating(long duration,
@@ -829,12 +839,16 @@
                     targetScale, targetCenterX, targetCenterY, callback);
             advanceTimeBy(duration);
         });
+        // wait for animation returns
+        waitForIdleSync();
     }
 
     private void deleteWindowMagnificationWithoutAnimation() {
         getInstrumentation().runOnMainSync(() -> {
             mWindowMagnificationAnimationController.deleteWindowMagnification(null);
         });
+        // wait for animation returns
+        waitForIdleSync();
     }
 
     private void deleteWindowMagnificationAndWaitAnimating(long duration,
@@ -843,6 +857,8 @@
             mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
             advanceTimeBy(duration);
         });
+        // wait for animation returns
+        waitForIdleSync();
     }
 
     private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 7c626a1..e0c6bba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -44,6 +44,8 @@
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -56,6 +58,7 @@
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import org.junit.Before
@@ -89,6 +92,9 @@
     @Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
     @Mock private lateinit var iStatusBarService: IStatusBarService
     @Mock private lateinit var headsUpManager: HeadsUpManager
+    private val activeNotificationsRepository = ActiveNotificationListRepository()
+    private val activeNotificationsInteractor =
+        ActiveNotificationsInteractor(activeNotificationsRepository, StandardTestDispatcher())
 
     private val keyguardRepository = FakeKeyguardRepository()
     private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
@@ -98,6 +104,7 @@
             keyguardRepository,
             headsUpManager,
             powerInteractor,
+            activeNotificationsInteractor,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 0ee0939..43f7c60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.biometrics
 
 import android.app.admin.DevicePolicyManager
+import android.content.pm.PackageManager
 import android.hardware.biometrics.BiometricAuthenticator
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricManager
@@ -79,6 +80,8 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
 
+private const val OP_PACKAGE_NAME = "biometric.testapp"
+
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -109,6 +112,8 @@
     lateinit var authController: AuthController
     @Mock
     lateinit var selectedUserInteractor: SelectedUserInteractor
+    @Mock
+    private lateinit var packageManager: PackageManager
 
     private val testScope = TestScope(StandardTestDispatcher())
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
@@ -134,6 +139,7 @@
     private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
 
     private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
+    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
 
     private var authContainer: TestAuthContainerView? = null
 
@@ -156,6 +162,9 @@
                         selectedUserInteractor,
                         testScope.backgroundScope,
                 )
+        // Set up default logo icon
+        whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
+        context.setMockPackageManager(packageManager)
     }
 
     @After
@@ -533,6 +542,7 @@
             mPromptInfo = PromptInfo().apply {
                 this.authenticators = authenticators
             }
+            mOpPackageName = OP_PACKAGE_NAME
         },
         testScope.backgroundScope,
         fingerprintProps,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index ec7ce63..b39e09d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -43,6 +43,7 @@
 
 private const val USER_ID = 9
 private const val CHALLENGE = 90L
+private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -102,7 +103,8 @@
                     PromptInfo().apply { isConfirmationRequested = case },
                     USER_ID,
                     CHALLENGE,
-                    PromptKind.Biometric()
+                    PromptKind.Biometric(),
+                    OP_PACKAGE_NAME
                 )
 
                 assertThat(isConfirmationRequired).isEqualTo(case)
@@ -120,7 +122,8 @@
                     PromptInfo().apply { isConfirmationRequested = case },
                     USER_ID,
                     CHALLENGE,
-                    PromptKind.Biometric()
+                    PromptKind.Biometric(),
+                    OP_PACKAGE_NAME
                 )
 
                 assertThat(isConfirmationRequired).isTrue()
@@ -133,17 +136,19 @@
             val kind = PromptKind.Pin
             val promptInfo = PromptInfo()
 
-            repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind)
+            repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind, OP_PACKAGE_NAME)
 
             assertThat(repository.kind.value).isEqualTo(kind)
             assertThat(repository.userId.value).isEqualTo(USER_ID)
             assertThat(repository.challenge.value).isEqualTo(CHALLENGE)
             assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo)
+            assertThat(repository.opPackageName.value).isEqualTo(OP_PACKAGE_NAME)
 
             repository.unsetPrompt()
 
             assertThat(repository.promptInfo.value).isNull()
             assertThat(repository.userId.value).isNull()
             assertThat(repository.challenge.value).isNull()
+            assertThat(repository.opPackageName.value).isNull()
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
index dcefea2..8a46c0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
@@ -30,6 +30,7 @@
 
 private const val USER_ID = 22
 private const val OPERATION_ID = 100L
+private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -114,7 +115,8 @@
                 },
                 kind = kind,
                 userId = USER_ID,
-                challenge = OPERATION_ID
+                challenge = OPERATION_ID,
+                opPackageName = OP_PACKAGE_NAME
             )
 
             assertThat(prompt?.title).isEqualTo(title)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index f15b738..52b4275 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -51,6 +51,7 @@
 
 private const val USER_ID = 8
 private const val CHALLENGE = 999L
+private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -113,13 +114,20 @@
 
         assertThat(currentPrompt).isNull()
 
-        interactor.useBiometricsForAuthentication(info, USER_ID, CHALLENGE, modalities)
+        interactor.useBiometricsForAuthentication(
+            info,
+            USER_ID,
+            CHALLENGE,
+            modalities,
+            OP_PACKAGE_NAME
+        )
 
         assertThat(currentPrompt).isNotNull()
         assertThat(currentPrompt?.title).isEqualTo(TITLE)
         assertThat(currentPrompt?.description).isEqualTo(DESCRIPTION)
         assertThat(currentPrompt?.subtitle).isEqualTo(SUBTITLE)
         assertThat(currentPrompt?.negativeButtonText).isEqualTo(NEGATIVE_TEXT)
+        assertThat(currentPrompt?.opPackageName).isEqualTo(OP_PACKAGE_NAME)
 
         if (allowCredentialFallback) {
             assertThat(credentialKind).isSameInstanceAs(PromptKind.Password)
@@ -167,7 +175,7 @@
 
         assertThat(currentPrompt).isNull()
 
-        interactor.useCredentialsForAuthentication(info, kind, USER_ID, CHALLENGE)
+        interactor.useCredentialsForAuthentication(info, kind, USER_ID, CHALLENGE, OP_PACKAGE_NAME)
 
         // not using biometrics, should be null with no fallback option
         assertThat(currentPrompt).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index bd4973d..a46167a42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.biometrics.domain.model
 
-import android.hardware.biometrics.PromptContentListItemBulletedText
+import android.graphics.Bitmap
+import android.hardware.biometrics.PromptContentItemBulletedText
 import android.hardware.biometrics.PromptVerticalListContentView
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -8,6 +9,7 @@
 import com.android.systemui.biometrics.promptInfo
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricUserInfo
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -15,6 +17,7 @@
 
 private const val USER_ID = 2
 private const val OPERATION_ID = 8L
+private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @SmallTest
 @RunWith(JUnit4::class)
@@ -22,19 +25,22 @@
 
     @Test
     fun biometricRequestFromPromptInfo() {
+        val logoRes = R.drawable.ic_cake
         val title = "what"
         val subtitle = "a"
         val description = "request"
         val contentView =
             PromptVerticalListContentView.Builder()
                 .setDescription("content description")
-                .addListItem(PromptContentListItemBulletedText("content text"))
+                .addListItem(PromptContentItemBulletedText("content item 1"))
+                .addListItem(PromptContentItemBulletedText("content item 2"), 1)
                 .build()
 
         val fpPros = fingerprintSensorPropertiesInternal().first()
         val request =
             BiometricPromptRequest.Biometric(
                 promptInfo(
+                    logoRes = logoRes,
                     title = title,
                     subtitle = subtitle,
                     description = description,
@@ -43,8 +49,10 @@
                 BiometricUserInfo(USER_ID),
                 BiometricOperationInfo(OPERATION_ID),
                 BiometricModalities(fingerprintProperties = fpPros),
+                OP_PACKAGE_NAME,
             )
 
+        assertThat(request.logoRes).isEqualTo(logoRes)
         assertThat(request.title).isEqualTo(title)
         assertThat(request.subtitle).isEqualTo(subtitle)
         assertThat(request.description).isEqualTo(description)
@@ -56,6 +64,23 @@
     }
 
     @Test
+    fun biometricRequestLogoBitmapFromPromptInfo() {
+        val logoBitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888)
+        val fpPros = fingerprintSensorPropertiesInternal().first()
+        val request =
+            BiometricPromptRequest.Biometric(
+                promptInfo(
+                    logoBitmap = logoBitmap,
+                ),
+                BiometricUserInfo(USER_ID),
+                BiometricOperationInfo(OPERATION_ID),
+                BiometricModalities(fingerprintProperties = fpPros),
+                OP_PACKAGE_NAME,
+            )
+        assertThat(request.logoBitmap).isEqualTo(logoBitmap)
+    }
+
+    @Test
     fun credentialRequestFromPromptInfo() {
         val title = "what"
         val subtitle = "a"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index bf61c2e..3888f2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import android.content.pm.PackageManager
 import android.content.res.Configuration
+import android.graphics.Bitmap
 import android.graphics.Point
-import android.hardware.biometrics.PromptContentListItemBulletedText
+import android.graphics.drawable.BitmapDrawable
+import android.hardware.biometrics.PromptContentItemBulletedText
 import android.hardware.biometrics.PromptContentView
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.PromptVerticalListContentView
@@ -76,6 +79,7 @@
 private const val USER_ID = 4
 private const val CHALLENGE = 2L
 private const val DELAY = 1000L
+private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -88,9 +92,14 @@
     @Mock private lateinit var authController: AuthController
     @Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var udfpsUtils: UdfpsUtils
+    @Mock private lateinit var packageManager: PackageManager
 
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val testScope = TestScope()
+    private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
+    private val logoResFromApp = R.drawable.ic_cake
+    private val logoFromApp = context.getDrawable(logoResFromApp)
+    private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
 
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
@@ -140,7 +149,8 @@
         selector.resetPrompt()
         promptContentView =
             PromptVerticalListContentView.Builder()
-                .addListItem(PromptContentListItemBulletedText("test"))
+                .addListItem(PromptContentItemBulletedText("content item 1"))
+                .addListItem(PromptContentItemBulletedText("content item 2"), 1)
                 .build()
 
         viewModel =
@@ -152,6 +162,12 @@
                 udfpsUtils
             )
         iconViewModel = viewModel.iconViewModel
+
+        // Set up default logo icon and app customized icon
+        whenever(packageManager.getApplicationIcon(OP_PACKAGE_NAME)).thenReturn(defaultLogoIcon)
+        context.setMockPackageManager(packageManager)
+        val resources = context.getOrCreateTestableResources()
+        resources.addOverride(logoResFromApp, logoFromApp)
     }
 
     @Test
@@ -1226,6 +1242,26 @@
             assertThat(contentView).isNull()
         }
 
+    @Test
+    fun defaultLogoIfNoLogoSet() = runGenericTest {
+        val logo by collectLastValue(viewModel.logo)
+        assertThat(logo).isEqualTo(defaultLogoIcon)
+    }
+
+    @Test
+    fun logoResSetByApp() =
+        runGenericTest(logoRes = logoResFromApp) {
+            val logo by collectLastValue(viewModel.logo)
+            assertThat(logo).isEqualTo(logoFromApp)
+        }
+
+    @Test
+    fun logoBitmapSetByApp() =
+        runGenericTest(logoBitmap = logoBitmapFromApp) {
+            val logo by collectLastValue(viewModel.logo)
+            assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
+        }
+
     /** Asserts that the selected buttons are visible now. */
     private suspend fun TestScope.assertButtonsVisible(
         tryAgain: Boolean = false,
@@ -1247,6 +1283,8 @@
         allowCredentialFallback: Boolean = false,
         description: String? = null,
         contentView: PromptContentView? = null,
+        logoRes: Int = -1,
+        logoBitmap: Bitmap? = null,
         block: suspend TestScope.() -> Unit
     ) {
         selector.initializePrompt(
@@ -1256,6 +1294,8 @@
             face = testCase.face,
             descriptionFromApp = description,
             contentViewFromApp = contentView,
+            logoResFromApp = logoRes,
+            logoBitmapFromApp = logoBitmap,
         )
 
         // put the view model in the initial authenticating state, unless explicitly skipped
@@ -1433,9 +1473,13 @@
     allowCredentialFallback: Boolean = false,
     descriptionFromApp: String? = null,
     contentViewFromApp: PromptContentView? = null,
+    logoResFromApp: Int = -1,
+    logoBitmapFromApp: Bitmap? = null,
 ) {
     val info =
         PromptInfo().apply {
+            logoRes = logoResFromApp
+            logoBitmap = logoBitmapFromApp
             title = "t"
             subtitle = "s"
             description = descriptionFromApp
@@ -1444,11 +1488,13 @@
             isDeviceCredentialAllowed = allowCredentialFallback
             isConfirmationRequested = requireConfirmation
         }
+
     useBiometricsForAuthentication(
         info,
         USER_ID,
         CHALLENGE,
         BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
+        OP_PACKAGE_NAME,
     )
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
index 44c57f3..134c40d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
@@ -3,8 +3,9 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.SystemClock
@@ -23,8 +24,8 @@
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var bouncerLogger: TableLogBuffer
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
 
     lateinit var underTest: KeyguardBouncerRepository
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
new file mode 100644
index 0000000..d397fc2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keyboard.stickykeys.ui.viewmodel
+
+import android.hardware.input.InputManager
+import android.hardware.input.StickyModifierState
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
+import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl
+import com.android.systemui.keyboard.stickykeys.shared.model.Locked
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
+import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+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.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+
+@SmallTest
+@RunWith(JUnit4::class)
+class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
+
+    private val dispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(dispatcher)
+    private lateinit var viewModel: StickyKeysIndicatorViewModel
+    private val inputManager = mock<InputManager>()
+    private val keyboardRepository = FakeKeyboardRepository()
+    private val captor =
+        ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
+
+    @Before
+    fun setup() {
+        val stickyKeysRepository = StickyKeysRepositoryImpl(
+            inputManager,
+            dispatcher,
+            mock<StickyKeysLogger>()
+        )
+        viewModel =
+            StickyKeysIndicatorViewModel(
+                stickyKeysRepository = stickyKeysRepository,
+                keyboardRepository = keyboardRepository,
+                applicationScope = testScope.backgroundScope,
+            )
+    }
+
+    @Test
+    fun startsListeningToStickyKeysOnlyWhenKeyboardIsConnected() {
+        testScope.runTest {
+            collectLastValue(viewModel.indicatorContent)
+            runCurrent()
+            verifyZeroInteractions(inputManager)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            runCurrent()
+
+            verify(inputManager)
+                .registerStickyModifierStateListener(
+                    any(),
+                    any(InputManager.StickyModifierStateListener::class.java)
+                )
+        }
+    }
+
+    @Test
+    fun stopsListeningToStickyKeysWhenKeyboardDisconnects() {
+        testScope.runTest {
+            collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            runCurrent()
+
+            keyboardRepository.setIsAnyKeyboardConnected(false)
+            runCurrent()
+
+            verify(inputManager).unregisterStickyModifierStateListener(any())
+        }
+    }
+
+    @Test
+    fun emitsStickyKeysListWhenStickyKeyIsPressed() {
+        testScope.runTest {
+            val stickyKeys by collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeys(mapOf(ALT to false))
+
+            assertThat(stickyKeys).isEqualTo(mapOf(ALT to Locked(false)))
+        }
+    }
+
+    @Test
+    fun emitsEmptyListWhenNoStickyKeysAreActive() {
+        testScope.runTest {
+            val stickyKeys by collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeys(emptyMap())
+
+            assertThat(stickyKeys).isEqualTo(emptyMap<ModifierKey, Locked>())
+        }
+    }
+
+    @Test
+    fun passesAllStickyKeysToDialog() {
+        testScope.runTest {
+            val stickyKeys by collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeys(mapOf(
+                ALT to false,
+                META to false,
+                SHIFT to false))
+
+            assertThat(stickyKeys).isEqualTo(mapOf(
+                ALT to Locked(false),
+                META to Locked(false),
+                SHIFT to Locked(false),
+            ))
+        }
+    }
+
+    @Test
+    fun showsOnlyLockedStateIfKeyIsStickyAndLocked() {
+        testScope.runTest {
+            val stickyKeys by collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeys(mapOf(
+                ALT to false,
+                ALT to true))
+
+            assertThat(stickyKeys).isEqualTo(mapOf(ALT to Locked(true)))
+        }
+    }
+
+    @Test
+    fun doesNotChangeOrderOfKeysIfTheyBecomeLocked() {
+        testScope.runTest {
+            val stickyKeys by collectLastValue(viewModel.indicatorContent)
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+
+            setStickyKeys(mapOf(
+                META to false,
+                SHIFT to false, // shift is sticky but not locked
+                CTRL to false))
+            val previousShiftIndex = stickyKeys?.toList()?.indexOf(SHIFT to Locked(false))
+
+            setStickyKeys(mapOf(
+                SHIFT to false,
+                SHIFT to true, // shift is now locked
+                META to false,
+                CTRL to false))
+            assertThat(stickyKeys?.toList()?.indexOf(SHIFT to Locked(true)))
+                .isEqualTo(previousShiftIndex)
+        }
+    }
+
+    private fun TestScope.setStickyKeys(keys: Map<ModifierKey, Boolean>) {
+        runCurrent()
+        verify(inputManager).registerStickyModifierStateListener(any(), captor.capture())
+        captor.value.onStickyModifierStateChanged(TestStickyModifierState(keys))
+        runCurrent()
+    }
+
+    private class TestStickyModifierState(private val keys: Map<ModifierKey, Boolean>) :
+        StickyModifierState() {
+
+        private fun isOn(key: ModifierKey) = keys.any { it.key == key && !it.value }
+        private fun isLocked(key: ModifierKey) = keys.any { it.key == key && it.value }
+
+        override fun isAltGrModifierLocked() = isLocked(ALT_GR)
+        override fun isAltGrModifierOn() = isOn(ALT_GR)
+        override fun isAltModifierLocked() = isLocked(ALT)
+        override fun isAltModifierOn() = isOn(ALT)
+        override fun isCtrlModifierLocked() = isLocked(CTRL)
+        override fun isCtrlModifierOn() = isOn(CTRL)
+        override fun isMetaModifierLocked() = isLocked(META)
+        override fun isMetaModifierOn() = isOn(META)
+        override fun isShiftModifierLocked() = isLocked(SHIFT)
+        override fun isShiftModifierOn() = isOn(SHIFT)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 0ea4e9f..8b6611f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -1342,14 +1342,8 @@
             runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.AOD)
             runCurrent()
 
-            // WHEN the keyguard is occluded and aod ends
+            // WHEN the keyguard is occluded
             keyguardRepository.setKeyguardOccluded(true)
-            keyguardRepository.setDozeTransitionModel(
-                DozeTransitionModel(
-                    from = DozeStateModel.DOZE_AOD,
-                    to = DozeStateModel.FINISH,
-                )
-            )
             runCurrent()
 
             val info =
@@ -1366,6 +1360,30 @@
         }
 
     @Test
+    fun aodToPrimaryBouncer() =
+        testScope.runTest {
+            // GIVEN a prior transition has run to AOD
+            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.AOD)
+            runCurrent()
+
+            // WHEN the primary bouncer is set to show
+            bouncerRepository.setPrimaryShow(true)
+            runCurrent()
+
+            val info =
+                withArgCaptor<TransitionInfo> {
+                    verify(transitionRepository).startTransition(capture())
+                }
+            // THEN a transition to OCCLUDED should occur
+            assertThat(info.ownerName).isEqualTo("FromAodTransitionInteractor")
+            assertThat(info.from).isEqualTo(KeyguardState.AOD)
+            assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+            assertThat(info.animator).isNotNull()
+
+            coroutineContext.cancelChildren()
+        }
+
+    @Test
     fun lockscreenToOccluded_fromCameraGesture() =
         testScope.runTest {
             // GIVEN a prior transition has run to LOCKSCREEN
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
new file mode 100644
index 0000000..60eb3ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.testing.AndroidTestingRunner
+import android.view.KeyEvent
+import android.view.KeyEvent.KEYCODE_DPAD_LEFT
+import android.view.View
+import androidx.core.util.Consumer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LeftRightArrowPressedListenerTest : SysuiTestCase() {
+
+    private lateinit var underTest: LeftRightArrowPressedListener
+    private val callback =
+        object : Consumer<Int> {
+            var lastValue: Int? = null
+
+            override fun accept(keyCode: Int?) {
+                lastValue = keyCode
+            }
+        }
+
+    private val view = View(context)
+
+    @Before
+    fun setUp() {
+        underTest = LeftRightArrowPressedListener.createAndRegisterListenerForView(view)
+        underTest.setArrowKeyPressedListener(callback)
+    }
+
+    @Test
+    fun shouldTriggerCallback_whenArrowUpReceived_afterArrowDownReceived() {
+        underTest.sendKey(KeyEvent.ACTION_DOWN, KEYCODE_DPAD_LEFT)
+
+        underTest.sendKey(KeyEvent.ACTION_UP, KEYCODE_DPAD_LEFT)
+
+        assertThat(callback.lastValue).isEqualTo(KEYCODE_DPAD_LEFT)
+    }
+
+    @Test
+    fun shouldNotTriggerCallback_whenKeyUpReceived_ifKeyDownNotReceived() {
+        underTest.sendKey(KeyEvent.ACTION_UP, KEYCODE_DPAD_LEFT)
+
+        assertThat(callback.lastValue).isNull()
+    }
+
+    @Test
+    fun shouldNotTriggerCallback_whenKeyUpReceived_ifKeyDownWasRepeated() {
+        underTest.sendKeyWithRepeat(KeyEvent.ACTION_UP, KEYCODE_DPAD_LEFT, repeat = 2)
+        underTest.sendKey(KeyEvent.ACTION_UP, KEYCODE_DPAD_LEFT)
+
+        assertThat(callback.lastValue).isNull()
+    }
+
+    @Test
+    fun shouldNotTriggerCallback_whenKeyUpReceived_ifKeyDownReceivedBeforeLosingFocus() {
+        underTest.sendKey(KeyEvent.ACTION_DOWN, KEYCODE_DPAD_LEFT)
+        underTest.onFocusChange(view, hasFocus = false)
+        underTest.onFocusChange(view, hasFocus = true)
+
+        underTest.sendKey(KeyEvent.ACTION_UP, KEYCODE_DPAD_LEFT)
+
+        assertThat(callback.lastValue).isNull()
+    }
+
+    private fun LeftRightArrowPressedListener.sendKey(action: Int, keyCode: Int) {
+        onKey(view, keyCode, KeyEvent(action, keyCode))
+    }
+
+    private fun LeftRightArrowPressedListener.sendKeyWithRepeat(
+        action: Int,
+        keyCode: Int,
+        repeat: Int
+    ) {
+        val keyEvent =
+            KeyEvent(
+                /* downTime= */ 0L,
+                /* eventTime= */ 0L,
+                /* action= */ action,
+                /* code= */ KEYCODE_DPAD_LEFT,
+                /* repeat= */ repeat
+            )
+        onKey(view, keyCode, keyEvent)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
index db9e548..8ef3f57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
@@ -2,11 +2,13 @@
 
 import android.content.Context
 import android.testing.AndroidTestingRunner
-import android.view.KeyEvent
 import android.view.View
 import android.widget.Scroller
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.PageIndicator.PageScrollActionListener
+import com.android.systemui.qs.PageIndicator.PageScrollActionListener.LEFT
+import com.android.systemui.qs.PageIndicator.PageScrollActionListener.RIGHT
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -22,7 +24,7 @@
 class PagedTileLayoutTest : SysuiTestCase() {
 
     @Mock private lateinit var pageIndicator: PageIndicator
-    @Captor private lateinit var captor: ArgumentCaptor<View.OnKeyListener>
+    @Captor private lateinit var captor: ArgumentCaptor<PageScrollActionListener>
 
     private lateinit var pageTileLayout: TestPagedTileLayout
     private lateinit var scroller: Scroller
@@ -32,7 +34,7 @@
         MockitoAnnotations.initMocks(this)
         pageTileLayout = TestPagedTileLayout(mContext)
         pageTileLayout.setPageIndicator(pageIndicator)
-        verify(pageIndicator).setOnKeyListener(captor.capture())
+        verify(pageIndicator).setPageScrollActionListener(captor.capture())
         setViewWidth(pageTileLayout, width = PAGE_WIDTH)
         scroller = pageTileLayout.mScroller
     }
@@ -43,28 +45,27 @@
     }
 
     @Test
-    fun scrollsRight_afterRightArrowPressed_whenFocusOnPagerIndicator() {
+    fun scrollsRight_afterRightScrollActionTriggered() {
         pageTileLayout.currentPageIndex = 0
 
-        sendUpEvent(KeyEvent.KEYCODE_DPAD_RIGHT)
+        sendScrollActionEvent(RIGHT)
 
         assertThat(scroller.isFinished).isFalse() // aka we're scrolling
         assertThat(scroller.finalX).isEqualTo(scroller.currX + PAGE_WIDTH)
     }
 
     @Test
-    fun scrollsLeft_afterLeftArrowPressed_whenFocusOnPagerIndicator() {
+    fun scrollsLeft_afterLeftScrollActionTriggered() {
         pageTileLayout.currentPageIndex = 1 // we won't scroll left if we're on the first page
 
-        sendUpEvent(KeyEvent.KEYCODE_DPAD_LEFT)
+        sendScrollActionEvent(LEFT)
 
         assertThat(scroller.isFinished).isFalse() // aka we're scrolling
         assertThat(scroller.finalX).isEqualTo(scroller.currX - PAGE_WIDTH)
     }
 
-    private fun sendUpEvent(keyCode: Int) {
-        val event = KeyEvent(KeyEvent.ACTION_UP, keyCode)
-        captor.value.onKey(pageIndicator, keyCode, event)
+    private fun sendScrollActionEvent(@PageScrollActionListener.Direction direction: Int) {
+        captor.value.onScrollActionTriggered(direction)
     }
 
     /**
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 49579f6..b3e386e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -113,6 +113,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.KeyguardMediaController;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
@@ -125,7 +126,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.shade.data.repository.ShadeAnimationRepository;
@@ -355,8 +355,8 @@
     protected FakeKeyguardRepository mFakeKeyguardRepository;
     protected KeyguardInteractor mKeyguardInteractor;
     protected ShadeAnimationInteractor mShadeAnimationInteractor;
-    protected SceneTestUtils mUtils = new SceneTestUtils(this);
-    protected TestScope mTestScope = mUtils.getTestScope();
+    protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+    protected TestScope mTestScope = mKosmos.getTestScope();
     protected ShadeInteractor mShadeInteractor;
     protected PowerInteractor mPowerInteractor;
     protected NotificationPanelViewController.TouchHandler mTouchHandler;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 971c8a3..a894f87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -73,11 +73,11 @@
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.scene.data.repository.SceneContainerRepository;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
@@ -150,8 +150,8 @@
 
     private final Executor mMainExecutor = MoreExecutors.directExecutor();
     private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
-    private final SceneTestUtils mUtils = new SceneTestUtils(this);
-    private final TestScope mTestScope = mUtils.getTestScope();
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+    private final TestScope mTestScope = mKosmos.getTestScope();
     private ShadeInteractor mShadeInteractor;
 
     private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -178,15 +178,15 @@
         FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
         FakeShadeRepository shadeRepository = new FakeShadeRepository();
 
-        mScreenOffAnimationController = mUtils.getScreenOffAnimationController();
-        mStatusBarStateController = spy(mUtils.getStatusBarStateController());
-        PowerInteractor powerInteractor = mUtils.powerInteractor();
+        mScreenOffAnimationController = mKosmos.getScreenOffAnimationController();
+        mStatusBarStateController = spy(mKosmos.getStatusBarStateController());
+        PowerInteractor powerInteractor = mKosmos.getPowerInteractor();
 
         SceneInteractor sceneInteractor = new SceneInteractor(
                 mTestScope.getBackgroundScope(),
                 new SceneContainerRepository(
                         mTestScope.getBackgroundScope(),
-                        mUtils.fakeSceneContainerConfig()),
+                        mKosmos.getFakeSceneContainerConfig()),
                 powerInteractor,
                 mock(SceneLogger.class));
 
@@ -219,8 +219,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 shadeRepository,
@@ -245,8 +245,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mKeyguardSecurityModel,
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 ca68fd8..cbd4d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -61,6 +61,7 @@
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.media.controls.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
@@ -68,7 +69,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.scene.data.repository.SceneContainerRepository;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
@@ -131,8 +131,8 @@
 
     protected QuickSettingsController mQsController;
 
-    protected SceneTestUtils mUtils = new SceneTestUtils(this);
-    protected TestScope mTestScope = mUtils.getTestScope();
+    protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+    protected TestScope mTestScope = mKosmos.getTestScope();
 
     @Mock protected Resources mResources;
     @Mock protected KeyguardBottomAreaView mQsFrame;
@@ -203,8 +203,8 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
-        mStatusBarStateController = mUtils.getStatusBarStateController();
-        mInteractionJankMonitor = mUtils.getInteractionJankMonitor();
+        mStatusBarStateController = mKosmos.getStatusBarStateController();
+        mInteractionJankMonitor = mKosmos.getInteractionJankMonitor();
 
         FakeDeviceProvisioningRepository deviceProvisioningRepository =
                 new FakeDeviceProvisioningRepository();
@@ -212,13 +212,13 @@
         FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
         FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
 
-        PowerInteractor powerInteractor = mUtils.powerInteractor();
+        PowerInteractor powerInteractor = mKosmos.getPowerInteractor();
 
         SceneInteractor sceneInteractor = new SceneInteractor(
                 mTestScope.getBackgroundScope(),
                 new SceneContainerRepository(
                         mTestScope.getBackgroundScope(),
-                        mUtils.fakeSceneContainerConfig()),
+                        mKosmos.getFakeSceneContainerConfig()),
                 powerInteractor,
                 mock(SceneLogger.class));
 
@@ -250,8 +250,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mShadeRepository,
@@ -276,8 +276,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mock(KeyguardSecurityModel.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 215f8b1..c4911a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -45,6 +47,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import dagger.Lazy
+import kotlinx.coroutines.test.StandardTestDispatcher
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -59,6 +62,8 @@
 @SmallTest
 class ShadeControllerImplTest : SysuiTestCase() {
     private val executor = FakeExecutor(FakeSystemClock())
+    private val testDispatcher = StandardTestDispatcher()
+    private val activeNotificationsRepository = ActiveNotificationListRepository()
 
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -84,6 +89,7 @@
             FakeKeyguardRepository(),
             headsUpManager,
             PowerInteractorFactory.create().powerInteractor,
+            ActiveNotificationsInteractor(activeNotificationsRepository, testDispatcher)
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 4a365b9..05e866e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -41,10 +41,12 @@
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.power.data.repository.FakePowerRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.shade.data.repository.FakeShadeRepository
@@ -56,6 +58,7 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.flow.emptyFlow
 import org.junit.Assert.assertEquals
@@ -71,17 +74,17 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class StatusBarStateControllerImplTest : SysuiTestCase() {
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
-    private val testDispatcher = utils.testDispatcher
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val testDispatcher = kosmos.testDispatcher
     private lateinit var shadeInteractor: ShadeInteractor
     private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
     private lateinit var fromPrimaryBouncerTransitionInteractor:
@@ -131,7 +134,7 @@
                 FakeKeyguardBouncerRepository(),
                 ConfigurationInteractor(configurationRepository),
                 shadeRepository,
-                utils::sceneInteractor
+                { kosmos.sceneInteractor },
             )
         val keyguardTransitionInteractor =
             KeyguardTransitionInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 65697b73..36f643a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -24,7 +24,6 @@
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -53,8 +52,8 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -78,28 +77,22 @@
 
     private lateinit var coordinator: ConversationCoordinator
 
-    private val featureFlags = FakeFeatureFlagsClassic()
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        coordinator = ConversationCoordinator(
-            peopleNotificationIdentifier,
-            conversationIconManager,
-            HighPriorityProvider(
+        coordinator =
+            ConversationCoordinator(
                 peopleNotificationIdentifier,
-                GroupMembershipManagerImpl(featureFlags)
-            ),
-            headerController
-        )
+                conversationIconManager,
+                HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()),
+                headerController
+            )
         whenever(channel.isImportantConversation).thenReturn(true)
 
         coordinator.attach(pipeline)
 
         // capture arguments:
-        promoter = withArgCaptor {
-            verify(pipeline).addPromoter(capture())
-        }
+        promoter = withArgCaptor { verify(pipeline).addPromoter(capture()) }
         beforeRenderListListener = withArgCaptor {
             verify(pipeline).addOnBeforeRenderListListener(capture())
         }
@@ -111,10 +104,10 @@
         entry = NotificationEntryBuilder().setChannel(channel).build()
 
         val section = NotifSection(peopleAlertingSectioner, 0)
-        entryA = NotificationEntryBuilder().setChannel(channel)
-            .setSection(section).setTag("A").build()
-        entryB = NotificationEntryBuilder().setChannel(channel)
-            .setSection(section).setTag("B").build()
+        entryA =
+            NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build()
+        entryB =
+            NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build()
     }
 
     @Test
@@ -129,11 +122,12 @@
         val altChildA = NotificationEntryBuilder().setTag("A").build()
         val altChildB = NotificationEntryBuilder().setTag("B").build()
         val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build()
-        val groupEntry = GroupEntryBuilder()
-            .setParent(GroupEntry.ROOT_ENTRY)
-            .setSummary(summary)
-            .setChildren(listOf(entry, altChildA, altChildB))
-            .build()
+        val groupEntry =
+            GroupEntryBuilder()
+                .setParent(GroupEntry.ROOT_ENTRY)
+                .setSummary(summary)
+                .setChildren(listOf(entry, altChildA, altChildB))
+                .build()
         assertTrue(promoter.shouldPromoteToTopLevel(entry))
         assertFalse(promoter.shouldPromoteToTopLevel(altChildA))
         assertFalse(promoter.shouldPromoteToTopLevel(altChildB))
@@ -146,41 +140,42 @@
     @Test
     fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
         // GIVEN
-        val alertingEntry = NotificationEntryBuilder().setChannel(channel)
-                .setImportance(IMPORTANCE_DEFAULT).build()
+        val alertingEntry =
+            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build()
         whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry))
-                .thenReturn(TYPE_PERSON)
+            .thenReturn(TYPE_PERSON)
 
         // put alerting people notifications in this section
         assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue()
-       }
+    }
 
     @Test
     fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
         // GIVEN
-        val silentEntry = NotificationEntryBuilder().setChannel(channel)
-                .setImportance(IMPORTANCE_LOW).build()
+        val silentEntry =
+            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
         whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
-                .thenReturn(TYPE_PERSON)
+            .thenReturn(TYPE_PERSON)
 
         // THEN put silent people notifications in this section
         assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue()
         // People Alerting sectioning happens before the silent one.
-        // It claims high important conversations and rest of conversations will be considered as silent.
+        // It claims high important conversations and rest of conversations will be considered as
+        // silent.
         assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isFalse()
     }
 
     @Test
     fun testNotInPeopleSection() {
         // GIVEN
-        val entry = NotificationEntryBuilder().setChannel(channel)
-                .setImportance(IMPORTANCE_LOW).build()
-        val importantEntry = NotificationEntryBuilder().setChannel(channel)
-                .setImportance(IMPORTANCE_HIGH).build()
+        val entry =
+            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
+        val importantEntry =
+            NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build()
         whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
-                .thenReturn(TYPE_NON_PERSON)
+            .thenReturn(TYPE_NON_PERSON)
         whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry))
-                .thenReturn(TYPE_NON_PERSON)
+            .thenReturn(TYPE_NON_PERSON)
 
         // THEN - only put people notification either silent or alerting
         assertThat(peopleSilentSectioner.isInSection(entry)).isFalse()
@@ -190,19 +185,23 @@
     @Test
     fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() {
         // GIVEN
-        val altChildA = NotificationEntryBuilder().setTag("A")
-                .setImportance(IMPORTANCE_DEFAULT).build()
-        val altChildB = NotificationEntryBuilder().setTag("B")
-                .setImportance(IMPORTANCE_LOW).build()
-        val summary = NotificationEntryBuilder().setId(2)
-                .setImportance(IMPORTANCE_LOW).setChannel(channel).build()
-        val groupEntry = GroupEntryBuilder()
+        val altChildA =
+            NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build()
+        val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build()
+        val summary =
+            NotificationEntryBuilder()
+                .setId(2)
+                .setImportance(IMPORTANCE_LOW)
+                .setChannel(channel)
+                .build()
+        val groupEntry =
+            GroupEntryBuilder()
                 .setParent(GroupEntry.ROOT_ENTRY)
                 .setSummary(summary)
                 .setChildren(listOf(altChildA, altChildB))
                 .build()
         whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary))
-                .thenReturn(TYPE_PERSON)
+            .thenReturn(TYPE_PERSON)
         // THEN
         assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
deleted file mode 100644
index c1ffa64..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerTest.kt
+++ /dev/null
@@ -1,173 +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.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.render
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags
-import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-
-@SmallTest
-class GroupMembershipManagerTest : SysuiTestCase() {
-    private lateinit var gmm: GroupMembershipManagerImpl
-
-    private val featureFlags = FakeFeatureFlagsClassic()
-
-    @Before
-    fun setUp() {
-        gmm = GroupMembershipManagerImpl(featureFlags)
-    }
-
-    @Test
-    fun testIsChildInGroup_topLevel() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
-        val topLevelEntry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
-        assertThat(gmm.isChildInGroup(topLevelEntry)).isFalse()
-    }
-
-    @Test
-    fun testIsChildInGroup_noParent_old() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
-        val noParentEntry = NotificationEntryBuilder().setParent(null).build()
-        assertThat(gmm.isChildInGroup(noParentEntry)).isTrue()
-    }
-
-    @Test
-    fun testIsChildInGroup_noParent_new() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-        val noParentEntry = NotificationEntryBuilder().setParent(null).build()
-        assertThat(gmm.isChildInGroup(noParentEntry)).isFalse()
-    }
-    @Test
-    fun testIsChildInGroup_summary_old() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
-
-        assertThat(gmm.isChildInGroup(summary)).isTrue()
-    }
-
-    @Test
-    fun testIsChildInGroup_summary_new() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
-
-        assertThat(gmm.isChildInGroup(summary)).isFalse()
-    }
-
-    @Test
-    fun testIsChildInGroup_child() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, false)
-        val childEntry = NotificationEntryBuilder().build()
-        assertThat(gmm.isChildInGroup(childEntry)).isTrue()
-    }
-
-    @Test
-    fun testIsGroupSummary_topLevelEntry() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-        val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
-        assertThat(gmm.isGroupSummary(entry)).isFalse()
-    }
-
-    @Test
-    fun testIsGroupSummary_summary() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
-
-        assertThat(gmm.isGroupSummary(summary)).isTrue()
-    }
-
-    @Test
-    fun testIsGroupSummary_child() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
-
-        assertThat(gmm.isGroupSummary(entry)).isFalse()
-    }
-
-    @Test
-    fun testGetGroupSummary_topLevelEntry() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-        val entry = NotificationEntryBuilder().setParent(GroupEntry.ROOT_ENTRY).build()
-        assertThat(gmm.getGroupSummary(entry)).isNull()
-    }
-
-    @Test
-    fun testGetGroupSummary_summary() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).build()
-
-        assertThat(gmm.getGroupSummary(summary)).isEqualTo(summary)
-    }
-
-    @Test
-    fun testGetGroupSummary_child() {
-        featureFlags.set(Flags.NOTIFICATION_GROUP_EXPANSION_CHANGE, true)
-
-        val groupKey = "group"
-        val summary =
-            NotificationEntryBuilder()
-                .setGroup(mContext, groupKey)
-                .setGroupSummary(mContext, true)
-                .build()
-        val entry = NotificationEntryBuilder().setGroup(mContext, groupKey).build()
-        GroupEntryBuilder().setKey(groupKey).setSummary(summary).addChild(entry).build()
-
-        assertThat(gmm.getGroupSummary(entry)).isEqualTo(summary)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 168e782..ff02ef3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -28,6 +28,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import android.app.Notification;
 import android.os.Handler;
 import android.os.Looper;
@@ -56,6 +58,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
 import com.android.systemui.statusbar.notification.logging.nano.Notifications;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -110,6 +114,11 @@
     private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
     private final PowerInteractor mPowerInteractor =
             PowerInteractorFactory.create().getPowerInteractor();
+    private final ActiveNotificationListRepository mActiveNotificationListRepository =
+            new ActiveNotificationListRepository();
+    private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
+            new ActiveNotificationsInteractor(mActiveNotificationListRepository,
+                    StandardTestDispatcher(null, null));
     private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
     private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
 
@@ -123,7 +132,8 @@
                 new WindowRootViewVisibilityRepository(mBarService, mUiBgExecutor),
                 mKeyguardRepository,
                 mHeadsUpManager,
-                mPowerInteractor);
+                mPowerInteractor,
+                mActiveNotificationsInteractor);
         mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
 
         mEntry = new NotificationEntryBuilder()
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 8a730cf..71613ed 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
@@ -42,6 +42,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static kotlinx.coroutines.test.TestCoroutineDispatchersKt.StandardTestDispatcher;
+
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -85,6 +87,8 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository;
+import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -156,6 +160,12 @@
 
     @Mock private UserManager mUserManager;
 
+    private final ActiveNotificationListRepository mActiveNotificationListRepository =
+            new ActiveNotificationListRepository();
+    private final ActiveNotificationsInteractor mActiveNotificationsInteractor =
+            new ActiveNotificationsInteractor(mActiveNotificationListRepository,
+                    StandardTestDispatcher(null, null));
+
     private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
 
     @Before
@@ -171,7 +181,8 @@
                 new WindowRootViewVisibilityRepository(mBarService, mExecutor),
                 new FakeKeyguardRepository(),
                 mHeadsUpManager,
-                PowerInteractorFactory.create().getPowerInteractor());
+                PowerInteractorFactory.create().getPowerInteractor(),
+                mActiveNotificationsInteractor);
 
         mGutsManager = new NotificationGutsManager(
                 mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 88662b6..89f826b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -66,6 +66,8 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlags;
+import com.android.systemui.scene.ui.view.WindowRootView;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -91,6 +93,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
 import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -112,6 +115,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import javax.inject.Provider;
+
 /**
  * Tests for {@link NotificationStackScrollLayoutController}.
  */
@@ -153,6 +158,9 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
     @Mock private ShadeController mShadeController;
+    @Mock private SceneContainerFlags mSceneContainerFlags;
+    @Mock private Provider<WindowRootView> mWindowRootView;
+    @Mock private NotificationStackAppearanceInteractor mNotificationStackAppearanceInteractor;
     @Mock private InteractionJankMonitor mJankMonitor;
     private final StackStateLogger mStackLogger = new StackStateLogger(logcatLogBuffer(),
             logcatLogBuffer());
@@ -724,6 +732,9 @@
                 mSeenNotificationsInteractor,
                 mViewBinder,
                 mShadeController,
+                mSceneContainerFlags,
+                mWindowRootView,
+                mNotificationStackAppearanceInteractor,
                 mJankMonitor,
                 mStackLogger,
                 mLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index a172120..4afcc8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -20,6 +20,7 @@
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION;
+import static com.android.systemui.Flags.FLAG_SCENE_CONTAINER;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
@@ -37,6 +38,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
@@ -51,6 +53,7 @@
 
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.SystemClock;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
@@ -74,6 +77,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -93,11 +97,13 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 
 import org.junit.Assert;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -947,6 +953,78 @@
         verify(runnable).run();
     }
 
+    @Test
+    public void testDispatchTouchEvent_sceneContainerDisabled() {
+        Assume.assumeFalse(SceneContainerFlag.isEnabled());
+
+        MotionEvent event = MotionEvent.obtain(
+                SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_MOVE,
+                0,
+                0,
+                0
+        );
+
+        mStackScroller.dispatchTouchEvent(event);
+
+        verify(mStackScrollLayoutController, never()).sendTouchToSceneFramework(any());
+    }
+
+    @Test
+    public void testDispatchTouchEvent_sceneContainerEnabled() {
+        Assume.assumeTrue(SceneContainerFlag.isEnabled());
+        mStackScroller.setIsBeingDragged(true);
+
+        MotionEvent moveEvent = MotionEvent.obtain(
+                SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_MOVE,
+                0,
+                0,
+                0
+        );
+        MotionEvent syntheticDownEvent = moveEvent.copy();
+        syntheticDownEvent.setAction(MotionEvent.ACTION_DOWN);
+        mStackScroller.dispatchTouchEvent(moveEvent);
+
+        verify(mStackScrollLayoutController).sendTouchToSceneFramework(argThat(
+                new MotionEventMatcher(syntheticDownEvent)));
+
+        mStackScroller.dispatchTouchEvent(moveEvent);
+
+        verify(mStackScrollLayoutController).sendTouchToSceneFramework(moveEvent);
+    }
+
+    @Test
+    @EnableFlags(FLAG_SCENE_CONTAINER)
+    public void testDispatchTouchEvent_sceneContainerEnabled_actionUp() {
+        Assume.assumeTrue(SceneContainerFlag.isEnabled());
+        mStackScroller.setIsBeingDragged(true);
+
+        MotionEvent upEvent = MotionEvent.obtain(
+                SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(),
+                MotionEvent.ACTION_UP,
+                0,
+                0,
+                0
+        );
+        MotionEvent syntheticDownEvent = upEvent.copy();
+        syntheticDownEvent.setAction(MotionEvent.ACTION_DOWN);
+
+        mStackScroller.dispatchTouchEvent(upEvent);
+
+        verify(mStackScrollLayoutController, atLeastOnce()).sendTouchToSceneFramework(argThat(
+                new MotionEventMatcher(syntheticDownEvent)));
+
+        mStackScroller.dispatchTouchEvent(upEvent);
+
+        verify(mStackScrollLayoutController, atLeastOnce()).sendTouchToSceneFramework(argThat(
+                new MotionEventMatcher(upEvent)));
+        assertFalse(mStackScroller.getIsBeingDragged());
+    }
+
     private void setBarStateForTest(int state) {
         // Can't inject this through the listener or we end up on the actual implementation
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
@@ -993,4 +1071,21 @@
                 /* metaState= */0
         );
     }
+
+    private static class MotionEventMatcher implements ArgumentMatcher<MotionEvent> {
+        private final MotionEvent mLeftEvent;
+
+        MotionEventMatcher(MotionEvent leftEvent) {
+            mLeftEvent = leftEvent;
+        }
+
+        @Override
+        public boolean matches(MotionEvent right) {
+            return mLeftEvent.getActionMasked() == right.getActionMasked()
+                    && mLeftEvent.getDownTime() == right.getDownTime()
+                    && mLeftEvent.getEventTime() == right.getEventTime()
+                    && mLeftEvent.getX() == right.getX()
+                    && mLeftEvent.getY() == right.getY();
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 91cbc32..7362e34 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -24,9 +24,9 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.policy.DevicePostureController
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.testKosmos
 import com.android.systemui.tuner.TunerService
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,8 +61,8 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class KeyguardBypassControllerTest : SysuiTestCase() {
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
     private val featureFlags = FakeFeatureFlags()
     private val shadeRepository = FakeShadeRepository()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 5fa7f13e..2d120cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -60,10 +60,10 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
 import com.android.systemui.res.R;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.shade.ShadeViewStateProvider;
 import com.android.systemui.shade.data.repository.FakeShadeRepository;
 import com.android.systemui.statusbar.CommandQueue;
@@ -149,7 +149,7 @@
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     private final TestScope mTestScope = TestScopeProvider.getTestScope();
     private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
-    private final SceneTestUtils mSceneTestUtils = new SceneTestUtils(this);
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     private KeyguardInteractor mKeyguardInteractor;
     private KeyguardStatusBarViewModel mViewModel;
 
@@ -166,11 +166,11 @@
                 mKeyguardRepository,
                 mCommandQueue,
                 PowerInteractorFactory.create().getPowerInteractor(),
-                mSceneTestUtils.getFakeSceneContainerFlags(),
+                mKosmos.getFakeSceneContainerFlags(),
                 new FakeKeyguardBouncerRepository(),
                 new ConfigurationInteractor(new FakeConfigurationRepository()),
                 new FakeShadeRepository(),
-                () -> mSceneTestUtils.sceneInteractor());
+                () -> mKosmos.getSceneInteractor());
         mViewModel =
                 new KeyguardStatusBarViewModel(
                         mTestScope.getBackgroundScope(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 5f9c096..ca0e526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -25,20 +25,22 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository
 import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor
 import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -47,20 +49,20 @@
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 class KeyguardStatusBarViewModelTest : SysuiTestCase() {
-    private val testScope = TestScope()
-    private val sceneTestUtils = SceneTestUtils(this)
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
     private val keyguardRepository = FakeKeyguardRepository()
     private val keyguardInteractor =
         KeyguardInteractor(
             keyguardRepository,
             mock<CommandQueue>(),
             PowerInteractorFactory.create().powerInteractor,
-            sceneTestUtils.fakeSceneContainerFlags,
+            kosmos.fakeSceneContainerFlags,
             FakeKeyguardBouncerRepository(),
             ConfigurationInteractor(FakeConfigurationRepository()),
             FakeShadeRepository(),
         ) {
-            sceneTestUtils.sceneInteractor()
+            kosmos.sceneInteractor
         }
     private val keyguardStatusBarInteractor =
         KeyguardStatusBarInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index e6b9d9b..b6a033a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -40,13 +40,18 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.res.R
-import com.android.systemui.scene.SceneTestUtils
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
+import com.android.systemui.telephony.domain.interactor.telephonyInteractor
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.source.UserRecord
@@ -98,8 +103,8 @@
     @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
-    private val utils = SceneTestUtils(this)
-    private val testScope = utils.testScope
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
     private lateinit var spyContext: Context
     private lateinit var userRepository: FakeUserRepository
     private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies
@@ -121,16 +126,17 @@
             SUPERVISED_USER_CREATION_APP_PACKAGE,
         )
 
-        utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+        kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
         mSetFlagsRule.enableFlags(AConfigFlags.FLAG_SWITCH_USER_ON_BG)
         spyContext = spy(context)
-        keyguardReply = KeyguardInteractorFactory.create(featureFlags = utils.fakeFeatureFlags)
+        keyguardReply =
+            KeyguardInteractorFactory.create(featureFlags = kosmos.fakeFeatureFlagsClassic)
         keyguardRepository = keyguardReply.repository
         userRepository = FakeUserRepository()
         refreshUsersScheduler =
             RefreshUsersScheduler(
                 applicationScope = testScope.backgroundScope,
-                mainDispatcher = utils.testDispatcher,
+                mainDispatcher = kosmos.testDispatcher,
                 repository = userRepository,
             )
     }
@@ -363,7 +369,7 @@
     fun actions_deviceUnlocked_fullScreen() {
         createUserInteractor()
         testScope.runTest {
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
             val userInfos = createUserInfos(count = 2, includeGuest = false)
 
             userRepository.setUserInfos(userInfos)
@@ -447,7 +453,7 @@
     fun actions_deviceLockedAddFromLockscreenSet_fullList_fullScreen() {
         createUserInteractor()
         testScope.runTest {
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
             val userInfos = createUserInfos(count = 2, includeGuest = false)
             userRepository.setUserInfos(userInfos)
             userRepository.setSelectedUserInfo(userInfos[0])
@@ -640,7 +646,7 @@
 
             val refreshUsersCallCount = userRepository.refreshUsersCallCount
 
-            utils.telephonyRepository.setCallState(1)
+            kosmos.fakeTelephonyRepository.setCallState(1)
             runCurrent()
 
             assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
@@ -792,7 +798,7 @@
     fun userRecordsFullScreen() {
         createUserInteractor()
         testScope.runTest {
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
             val userInfos = createUserInfos(count = 3, includeGuest = false)
             userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
             userRepository.setUserInfos(userInfos)
@@ -901,7 +907,7 @@
     fun showUserSwitcher_fullScreenEnabled_launchesFullScreenDialog() {
         createUserInteractor()
         testScope.runTest {
-            utils.fakeFeatureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
 
             val expandable = mock<Expandable>()
             underTest.showUserSwitcher(expandable)
@@ -1116,19 +1122,19 @@
                 manager = manager,
                 headlessSystemUserMode = headlessSystemUserMode,
                 applicationScope = testScope.backgroundScope,
-                telephonyInteractor = utils.telephonyInteractor(),
+                telephonyInteractor = kosmos.telephonyInteractor,
                 broadcastDispatcher = fakeBroadcastDispatcher,
                 keyguardUpdateMonitor = keyguardUpdateMonitor,
-                backgroundDispatcher = utils.testDispatcher,
-                mainDispatcher = utils.testDispatcher,
+                backgroundDispatcher = kosmos.testDispatcher,
+                mainDispatcher = kosmos.testDispatcher,
                 activityManager = activityManager,
                 refreshUsersScheduler = refreshUsersScheduler,
                 guestUserInteractor =
                     GuestUserInteractor(
                         applicationContext = spyContext,
                         applicationScope = testScope.backgroundScope,
-                        mainDispatcher = utils.testDispatcher,
-                        backgroundDispatcher = utils.testDispatcher,
+                        mainDispatcher = kosmos.testDispatcher,
+                        backgroundDispatcher = kosmos.testDispatcher,
                         manager = manager,
                         repository = userRepository,
                         deviceProvisionedController = deviceProvisionedController,
@@ -1139,7 +1145,7 @@
                         resetOrExitSessionReceiver = resetOrExitSessionReceiver,
                     ),
                 uiEventLogger = uiEventLogger,
-                featureFlags = utils.fakeFeatureFlags,
+                featureFlags = kosmos.fakeFeatureFlagsClassic,
                 userRestrictionChecker = mock(),
             )
     }
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 8920d4d..1ed045f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -117,11 +117,11 @@
 import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.scene.FakeWindowRootViewComponent;
-import com.android.systemui.scene.SceneTestUtils;
 import com.android.systemui.scene.data.repository.SceneContainerRepository;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags;
@@ -356,8 +356,8 @@
     @Mock
     private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
 
-    private final SceneTestUtils mUtils = new SceneTestUtils(this);
-    private final TestScope mTestScope = mUtils.getTestScope();
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+    private final TestScope mTestScope = mKosmos.getTestScope();
     private ShadeInteractor mShadeInteractor;
     private ShellTaskOrganizer mShellTaskOrganizer;
     private TaskViewTransitions mTaskViewTransitions;
@@ -408,8 +408,8 @@
         FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
 
         PowerInteractor powerInteractor = new PowerInteractor(
-                mUtils.getPowerRepository(),
-                mUtils.falsingCollector(),
+                mKosmos.getPowerRepository(),
+                mKosmos.getFalsingCollector(),
                 mock(ScreenOffAnimationController.class),
                 mStatusBarStateController);
 
@@ -417,7 +417,7 @@
                 mTestScope.getBackgroundScope(),
                 new SceneContainerRepository(
                         mTestScope.getBackgroundScope(),
-                        mUtils.fakeSceneContainerConfig()),
+                        mKosmos.getFakeSceneContainerConfig()),
                 powerInteractor,
                 mock(SceneLogger.class));
 
@@ -449,8 +449,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 shadeRepository,
@@ -475,8 +475,8 @@
                 keyguardTransitionRepository,
                 keyguardTransitionInteractor,
                 mTestScope.getBackgroundScope(),
-                mUtils.getTestDispatcher(),
-                mUtils.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
+                mKosmos.getTestDispatcher(),
                 keyguardInteractor,
                 featureFlags,
                 mock(KeyguardSecurityModel.class),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index 42ec8fed..f192de2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -26,12 +26,24 @@
     private val _isConfirmationRequired = MutableStateFlow(false)
     override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
 
+    private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
+    override val opPackageName = _opPackageName.asStateFlow()
+
     override fun setPrompt(
         promptInfo: PromptInfo,
         userId: Int,
         gatekeeperChallenge: Long?,
         kind: PromptKind,
-    ) = setPrompt(promptInfo, userId, gatekeeperChallenge, kind, forceConfirmation = false)
+        opPackageName: String,
+    ) =
+        setPrompt(
+            promptInfo,
+            userId,
+            gatekeeperChallenge,
+            kind,
+            forceConfirmation = false,
+            opPackageName = opPackageName
+        )
 
     fun setPrompt(
         promptInfo: PromptInfo,
@@ -39,12 +51,14 @@
         gatekeeperChallenge: Long?,
         kind: PromptKind,
         forceConfirmation: Boolean = false,
+        opPackageName: String? = null,
     ) {
         _promptInfo.value = promptInfo
         _userId.value = userId
         _challenge.value = gatekeeperChallenge
         _kind.value = kind
         _isConfirmationRequired.value = promptInfo.isConfirmationRequested || forceConfirmation
+        _opPackageName.value = opPackageName
     }
 
     override fun unsetPrompt() {
diff --git a/core/java/android/hardware/biometrics/PromptContentListItem.java b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryKosmos.kt
similarity index 60%
copy from core/java/android/hardware/biometrics/PromptContentListItem.java
copy to packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryKosmos.kt
index fa3783d..79107cc 100644
--- a/core/java/android/hardware/biometrics/PromptContentListItem.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,18 +12,13 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
+ *
  */
 
-package android.hardware.biometrics;
+package com.android.systemui.communal.data.repository
 
-import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import com.android.systemui.kosmos.Kosmos
 
-import android.annotation.FlaggedApi;
-
-/**
- * A list item shown on {@link PromptVerticalListContentView}.
- */
-@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
-public interface PromptContentListItem {
-}
-
+var Kosmos.communalPrefsRepository: CommunalPrefsRepository by
+    Kosmos.Fixture { fakeCommunalPrefsRepository }
+val Kosmos.fakeCommunalPrefsRepository by Kosmos.Fixture { FakeCommunalPrefsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
new file mode 100644
index 0000000..d3ed58b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.communal.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Fake implementation of [CommunalPrefsRepository] */
+class FakeCommunalPrefsRepository : CommunalPrefsRepository {
+    private val _isCtaDismissed = MutableStateFlow(false)
+    override val isCtaDismissed: Flow<Boolean> = _isCtaDismissed.asStateFlow()
+
+    override suspend fun setCtaDismissedForCurrentUser() {
+        _isCtaDismissed.value = true
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index e82cae4..c85c27e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -9,7 +9,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
@@ -53,12 +52,4 @@
     fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
         _isCommunalHubShowing.value = isCommunalHubShowing
     }
-
-    private val _isCtaTileInViewModeVisible: MutableStateFlow<Boolean> = MutableStateFlow(true)
-    override val isCtaTileInViewModeVisible: Flow<Boolean> =
-        _isCtaTileInViewModeVisible.asStateFlow()
-
-    override fun setCtaTileInViewModeVisibility(isVisible: Boolean) {
-        _isCtaTileInViewModeVisible.value = isVisible
-    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
index 95ff889..126bd6f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -17,11 +17,12 @@
 
 package com.android.systemui.communal.domain.interactor
 
-import android.appwidget.AppWidgetHost
 import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
+import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -42,7 +43,8 @@
         mediaRepository: FakeCommunalMediaRepository = FakeCommunalMediaRepository(),
         smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
         tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
-        appWidgetHost: AppWidgetHost = mock(),
+        communalPrefsRepository: FakeCommunalPrefsRepository = FakeCommunalPrefsRepository(),
+        appWidgetHost: CommunalAppWidgetHost = mock(),
         editWidgetsActivityStarter: EditWidgetsActivityStarter = mock(),
     ): WithDependencies {
         val withDeps =
@@ -55,6 +57,7 @@
             testScope,
             communalRepository,
             widgetRepository,
+            communalPrefsRepository,
             mediaRepository,
             smartspaceRepository,
             tutorialRepository,
@@ -66,6 +69,7 @@
             CommunalInteractor(
                 communalRepository,
                 widgetRepository,
+                communalPrefsRepository,
                 mediaRepository,
                 smartspaceRepository,
                 withDeps.keyguardInteractor,
@@ -79,13 +83,14 @@
         val testScope: TestScope,
         val communalRepository: FakeCommunalRepository,
         val widgetRepository: FakeCommunalWidgetRepository,
+        val communalPrefsRepository: FakeCommunalPrefsRepository,
         val mediaRepository: FakeCommunalMediaRepository,
         val smartspaceRepository: FakeSmartspaceRepository,
         val tutorialRepository: FakeCommunalTutorialRepository,
         val keyguardRepository: FakeKeyguardRepository,
         val keyguardInteractor: KeyguardInteractor,
         val tutorialInteractor: CommunalTutorialInteractor,
-        val appWidgetHost: AppWidgetHost,
+        val appWidgetHost: CommunalAppWidgetHost,
         val editWidgetsActivityStarter: EditWidgetsActivityStarter,
         val communalInteractor: CommunalInteractor,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 649b373..7cbbaab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.domain.interactor
 
 import com.android.systemui.communal.data.repository.communalMediaRepository
+import com.android.systemui.communal.data.repository.communalPrefsRepository
 import com.android.systemui.communal.data.repository.communalRepository
 import com.android.systemui.communal.data.repository.communalWidgetRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -30,6 +31,7 @@
         communalRepository = communalRepository,
         widgetRepository = communalWidgetRepository,
         mediaRepository = communalMediaRepository,
+        communalPrefsRepository = communalPrefsRepository,
         smartspaceRepository = smartspaceRepository,
         appWidgetHost = mock(),
         keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/CommunalInteractorKosmos.kt
index 59f0ec3..ffca83b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/CommunalInteractorKosmos.kt
@@ -16,11 +16,12 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.appwidget.AppWidgetHost
 import com.android.systemui.communal.data.repository.communalMediaRepository
+import com.android.systemui.communal.data.repository.communalPrefsRepository
 import com.android.systemui.communal.data.repository.communalRepository
 import com.android.systemui.communal.data.repository.communalWidgetRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.smartspace.data.repository.smartspaceRepository
@@ -32,9 +33,10 @@
             communalRepository = communalRepository,
             widgetRepository = communalWidgetRepository,
             mediaRepository = communalMediaRepository,
+            communalPrefsRepository = communalPrefsRepository,
             smartspaceRepository = smartspaceRepository,
             keyguardInteractor = keyguardInteractor,
-            appWidgetHost = mock(AppWidgetHost::class.java),
+            appWidgetHost = mock(CommunalAppWidgetHost::class.java),
             editWidgetsActivityStarter = mock(EditWidgetsActivityStarter::class.java),
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
new file mode 100644
index 0000000..24670b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.kosmos
+
+import android.content.applicationContext
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.bouncerRepository
+import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
+import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.sceneContainerConfig
+import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
+import com.android.systemui.statusbar.phone.screenOffAnimationController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.util.time.systemClock
+
+/** Helper for using [Kosmos] from Java. */
+@Deprecated("Please convert your test to Kotlin and use [Kosmos] directly.")
+class KosmosJavaAdapter(
+    testCase: SysuiTestCase,
+) {
+
+    private val kosmos = Kosmos()
+
+    val testDispatcher by lazy { kosmos.testDispatcher }
+    val testScope by lazy { kosmos.testScope }
+    val fakeFeatureFlags by lazy { kosmos.fakeFeatureFlagsClassic }
+    val fakeSceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
+    val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+    val configurationInteractor by lazy { kosmos.configurationInteractor }
+    val bouncerRepository by lazy { kosmos.bouncerRepository }
+    val communalRepository by lazy { kosmos.fakeCommunalRepository }
+    val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    val powerRepository by lazy { kosmos.fakePowerRepository }
+    val clock by lazy { kosmos.systemClock }
+    val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
+    val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
+    val statusBarStateController by lazy { kosmos.statusBarStateController }
+    val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
+    val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
+    val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
+    val sceneInteractor by lazy { kosmos.sceneInteractor }
+    val falsingCollector by lazy { kosmos.falsingCollector }
+    val powerInteractor by lazy { kosmos.powerInteractor }
+
+    init {
+        kosmos.applicationContext = testCase.context
+        kosmos.testCase = testCase
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
deleted file mode 100644
index d314a25..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ /dev/null
@@ -1,186 +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.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.scene
-
-import android.content.Context
-import android.content.applicationContext
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.domain.interactor.authenticationInteractor
-import com.android.systemui.bouncer.data.repository.bouncerRepository
-import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.classifier.domain.interactor.FalsingInteractor
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
-import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.jank.interactionJankMonitor
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.statusbar.statusBarStateController
-import com.android.systemui.power.data.repository.fakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.scene.data.repository.SceneContainerRepository
-import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneContainerConfig
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
-import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
-import com.android.systemui.statusbar.phone.screenOffAnimationController
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
-import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
-import com.android.systemui.telephony.domain.interactor.telephonyInteractor
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.user.domain.interactor.selectedUserInteractor
-import com.android.systemui.util.time.systemClock
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/**
- * Utilities for creating scene container framework related repositories, interactors, and
- * view-models for tests.
- */
-@OptIn(ExperimentalCoroutinesApi::class)
-@Deprecated("Please use Kosmos instead.")
-class SceneTestUtils {
-
-    val kosmos = Kosmos()
-
-    constructor(
-        context: Context,
-    ) {
-        kosmos.applicationContext = context
-    }
-
-    constructor(testCase: SysuiTestCase) : this(context = testCase.context) {
-        kosmos.testCase = testCase
-    }
-
-    val testDispatcher by lazy { kosmos.testDispatcher }
-    val testScope by lazy { kosmos.testScope }
-    val fakeFeatureFlags by lazy { kosmos.fakeFeatureFlagsClassic }
-    val fakeSceneContainerFlags by lazy { kosmos.fakeSceneContainerFlags }
-    val deviceEntryRepository by lazy { kosmos.fakeDeviceEntryRepository }
-    val authenticationRepository by lazy { kosmos.fakeAuthenticationRepository }
-    val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
-    val configurationInteractor by lazy { kosmos.configurationInteractor }
-    val telephonyRepository by lazy { kosmos.fakeTelephonyRepository }
-    val bouncerRepository by lazy { kosmos.bouncerRepository }
-    val communalRepository by lazy { kosmos.fakeCommunalRepository }
-    val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
-    val powerRepository by lazy { kosmos.fakePowerRepository }
-    val simBouncerRepository by lazy { kosmos.fakeSimBouncerRepository }
-    val clock by lazy { kosmos.systemClock }
-    val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
-    val simBouncerInteractor by lazy { kosmos.simBouncerInteractor }
-    val statusBarStateController by lazy { kosmos.statusBarStateController }
-    val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
-    val screenOffAnimationController by lazy { kosmos.screenOffAnimationController }
-
-    fun fakeSceneContainerRepository(): SceneContainerRepository {
-        return kosmos.sceneContainerRepository
-    }
-
-    fun fakeSceneKeys(): List<SceneKey> {
-        return kosmos.sceneKeys
-    }
-
-    fun fakeSceneContainerConfig(): SceneContainerConfig {
-        return kosmos.sceneContainerConfig
-    }
-
-    fun sceneInteractor(): SceneInteractor {
-        return kosmos.sceneInteractor
-    }
-
-    fun deviceEntryInteractor(): DeviceEntryInteractor {
-        return kosmos.deviceEntryInteractor
-    }
-
-    fun authenticationInteractor(): AuthenticationInteractor {
-        return kosmos.authenticationInteractor
-    }
-
-    fun keyguardInteractor(): KeyguardInteractor {
-        return kosmos.keyguardInteractor
-    }
-
-    fun communalInteractor(): CommunalInteractor {
-        return kosmos.communalInteractor
-    }
-
-    fun bouncerInteractor(): BouncerInteractor {
-        return kosmos.bouncerInteractor
-    }
-
-    fun notificationsPlaceholderViewModel(): NotificationsPlaceholderViewModel {
-        return kosmos.notificationsPlaceholderViewModel
-    }
-
-    fun bouncerViewModel(): BouncerViewModel {
-        return kosmos.bouncerViewModel
-    }
-
-    fun telephonyInteractor(): TelephonyInteractor {
-        return kosmos.telephonyInteractor
-    }
-
-    fun falsingInteractor(): FalsingInteractor {
-        return kosmos.falsingInteractor
-    }
-
-    fun falsingCollector(): FalsingCollector {
-        return kosmos.falsingCollector
-    }
-
-    fun powerInteractor(): PowerInteractor {
-        return kosmos.powerInteractor
-    }
-
-    fun selectedUserInteractor(): SelectedUserInteractor {
-        return kosmos.selectedUserInteractor
-    }
-
-    fun bouncerActionButtonInteractor(): BouncerActionButtonInteractor {
-        return kosmos.bouncerActionButtonInteractor
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/EntryUtil.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
index dda7fad..4efcada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/GroupEntryBuilder.java
@@ -52,32 +52,38 @@
         return ge;
     }
 
+    /** Sets the group key. */
     public GroupEntryBuilder setKey(String key) {
         mKey = key;
         return this;
     }
 
+    /** Sets the creation time. */
     public GroupEntryBuilder setCreationTime(long creationTime) {
         mCreationTime = creationTime;
         return this;
     }
 
+    /** Sets the parent entry of the group. */
     public GroupEntryBuilder setParent(@Nullable GroupEntry entry) {
         mParent = entry;
         return this;
     }
 
+    /** Sets the section the group belongs to. */
     public GroupEntryBuilder setSection(@Nullable NotifSection section) {
         mNotifSection = section;
         return this;
     }
 
+    /** Sets the group summary. */
     public GroupEntryBuilder setSummary(
             NotificationEntry summary) {
         mSummary = summary;
         return this;
     }
 
+    /** Sets the group children. */
     public GroupEntryBuilder setChildren(List<NotificationEntry> children) {
         mChildren.clear();
         mChildren.addAll(children);
@@ -90,6 +96,7 @@
         return this;
     }
 
+    /** Get the group's internal children list. */
     public static List<NotificationEntry> getRawChildren(GroupEntry groupEntry) {
         return groupEntry.getRawChildren();
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 0dbade7..d7e948e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.scene.shared.flag.sceneContainerFlags
+import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 
 val Kosmos.notificationsPlaceholderViewModel by Fixture {
     NotificationsPlaceholderViewModel(
         interactor = notificationStackAppearanceInteractor,
+        shadeInteractor = shadeInteractor,
         flags = sceneContainerFlags,
         featureFlags = featureFlagsClassic,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
index d9beabb..d80ee75 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.data.repository.windowRootViewVisibilityRepository
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
 import com.android.systemui.statusbar.policy.headsUpManager
 
 val Kosmos.windowRootViewVisibilityInteractor by Fixture {
@@ -32,5 +33,6 @@
         keyguardRepository = keyguardRepository,
         headsUpManager = headsUpManager,
         powerInteractor = powerInteractor,
+        activeNotificationsInteractor = activeNotificationsInteractor,
     )
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a856f42..f3b74ea 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1735,6 +1735,7 @@
 
         processResponseLockedForPcc(response, response.getClientState(), requestFlags);
         mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
+        mFillResponseEventLogger.logAndEndEvent();
     }
 
 
@@ -1847,6 +1848,33 @@
             return;
         }
         synchronized (mLock) {
+            // TODO(b/319913595): refactor logging for fill response for primary and secondary
+            //  providers
+            // Start a new FillResponse logger for the success case.
+            mFillResponseEventLogger.startLogForNewResponse();
+            mFillResponseEventLogger.maybeSetRequestId(fillResponse.getRequestId());
+            mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+            mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
+            mFillResponseEventLogger.startResponseProcessingTime();
+            // Time passed since session was created
+            final long fillRequestReceivedRelativeTimestamp =
+                    SystemClock.elapsedRealtime() - mLatencyBaseTime;
+            mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
+                    (int) (fillRequestReceivedRelativeTimestamp));
+            mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+                    (int) (fillRequestReceivedRelativeTimestamp));
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: "
+                        + id + " destroyed");
+                mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED);
+                mFillResponseEventLogger.logAndEndEvent();
+                return;
+            }
+
+            List<Dataset> datasetList = fillResponse.getDatasets();
+            int datasetCount = (datasetList == null) ? 0 : datasetList.size();
+            mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
+            mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
             if (mSecondaryResponses == null) {
                 mSecondaryResponses = new SparseArray<>(2);
             }
@@ -1859,6 +1887,8 @@
             if (currentView != null) {
                 currentView.maybeCallOnFillReady(flags);
             }
+            mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
+            mFillResponseEventLogger.logAndEndEvent();
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/transport/SecureTransport.java b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
index a0301a9..6e906eb 100644
--- a/services/companion/java/com/android/server/companion/transport/SecureTransport.java
+++ b/services/companion/java/com/android/server/companion/transport/SecureTransport.java
@@ -34,7 +34,7 @@
 
     private volatile boolean mShouldProcessRequests = false;
 
-    private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(100);
+    private final BlockingQueue<byte[]> mRequestQueue = new ArrayBlockingQueue<>(500);
 
     SecureTransport(int associationId, ParcelFileDescriptor fd, Context context) {
         super(associationId, fd, context);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 5a44ac8..9d9e7c9 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1387,6 +1387,8 @@
                 case BatteryManager.BATTERY_PROPERTY_MANUFACTURING_DATE:
                 case BatteryManager.BATTERY_PROPERTY_FIRST_USAGE_DATE:
                 case BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY:
+                case BatteryManager.BATTERY_PROPERTY_SERIAL_NUMBER:
+                case BatteryManager.BATTERY_PROPERTY_PART_STATUS:
                     mContext.enforceCallingPermission(
                             android.Manifest.permission.BATTERY_STATS, null);
                     break;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index eb6fdd7..f921b0b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -418,6 +418,8 @@
             LinkCapacityEstimate.INVALID, LinkCapacityEstimate.INVALID)));
     private List<List<LinkCapacityEstimate>> mLinkCapacityEstimateLists;
 
+    private int[] mSimultaneousCellularCallingSubIds = {};
+
     private int[] mECBMReason;
     private boolean[] mECBMStarted;
     private int[] mSCBMReason;
@@ -564,7 +566,9 @@
                 || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)
+                || events.contains(TelephonyCallback
+                        .EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -1427,6 +1431,15 @@
                         remove(r.binder);
                     }
                 }
+                if (events.contains(TelephonyCallback
+                        .EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED)) {
+                    try {
+                        r.callback.onSimultaneousCallingStateChanged(
+                                mSimultaneousCellularCallingSubIds);
+                    } catch (RemoteException ex) {
+                        remove(r.binder);
+                    }
+                }
                 if (events.contains(
                         TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
                     try {
@@ -3092,6 +3105,43 @@
         }
     }
 
+    /**
+     * Notify the listeners that simultaneous cellular calling subscriptions have changed
+     * @param subIds The set of subIds that support simultaneous cellular calling
+     */
+    public void notifySimultaneousCellularCallingSubscriptionsChanged(int[] subIds) {
+        if (!checkNotifyPermission("notifySimultaneousCellularCallingSubscriptionsChanged()")) {
+            return;
+        }
+
+        if (VDBG) {
+            StringBuilder b = new StringBuilder();
+            b.append("notifySimultaneousCellularCallingSubscriptionsChanged: ");
+            b.append("subIds = {");
+            for (int i : subIds) {
+                b.append(" ");
+                b.append(i);
+            }
+            b.append("}");
+            log(b.toString());
+        }
+
+        synchronized (mRecords) {
+            mSimultaneousCellularCallingSubIds = subIds;
+            for (Record r : mRecords) {
+                if (r.matchTelephonyCallbackEvent(TelephonyCallback
+                        .EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED)) {
+                    try {
+                        r.callback.onSimultaneousCallingStateChanged(subIds);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
     @Override
     public void addCarrierPrivilegesCallback(
             int phoneId,
diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java
index 5189017..b084cf3 100644
--- a/services/core/java/com/android/server/app/GameManagerSettings.java
+++ b/services/core/java/com/android/server/app/GameManagerSettings.java
@@ -251,6 +251,7 @@
                             + type);
                 }
             }
+            str.close();
         } catch (XmlPullParserException | java.io.IOException e) {
             Slog.wtf(TAG, "Error reading game manager settings", e);
             return false;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index df8d9e1..8fa0089 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -65,6 +65,9 @@
 import static android.app.AppOpsManager.opToName;
 import static android.app.AppOpsManager.opToPublicName;
 import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
 
@@ -973,7 +976,29 @@
             String pkgName = intent.getData().getEncodedSchemeSpecificPart();
             int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
 
-            if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
+            if (action.equals(ACTION_PACKAGE_ADDED)
+                    && !intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+                PackageInfo pi = getPackageManagerInternal().getPackageInfo(pkgName,
+                        PackageManager.GET_PERMISSIONS, Process.myUid(),
+                        UserHandle.getUserId(uid));
+                boolean isSamplingTarget = isSamplingTarget(pi);
+                synchronized (AppOpsService.this) {
+                    if (isSamplingTarget) {
+                        mRarelyUsedPackages.add(pkgName);
+                    }
+                    UidState uidState = getUidStateLocked(uid, true);
+                    if (!uidState.pkgOps.containsKey(pkgName)) {
+                        uidState.pkgOps.put(pkgName,
+                                new Ops(pkgName, uidState));
+                    }
+
+                    createSandboxUidStateIfNotExistsForAppLocked(uid);
+                }
+            } else if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) {
+                synchronized (AppOpsService.this) {
+                    packageRemovedLocked(uid, pkgName);
+                }
+            } else if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
                 AndroidPackage pkg = getPackageManagerInternal().getPackage(pkgName);
                 if (pkg == null) {
                     return;
@@ -1052,7 +1077,9 @@
         mHistoricalRegistry.systemReady(mContext.getContentResolver());
 
         IntentFilter packageUpdateFilter = new IntentFilter();
+        packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED);
         packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED);
         packageUpdateFilter.addDataScheme("package");
 
         mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
@@ -1079,7 +1106,7 @@
 
                     String action;
                     if (!ArrayUtils.contains(pkgsInUid, pkg)) {
-                        action = Intent.ACTION_PACKAGE_REMOVED;
+                        action = ACTION_PACKAGE_REMOVED;
                     } else {
                         action = Intent.ACTION_PACKAGE_REPLACED;
                     }
@@ -1160,44 +1187,6 @@
 
                     // onUserRemoved handled by #removeUser
                 });
-
-        getPackageManagerInternal().getPackageList(
-                new PackageManagerInternal.PackageListObserver() {
-                    @Override
-                    public void onPackageAdded(String packageName, int appId) {
-                        PackageInfo pi = getPackageManagerInternal().getPackageInfo(packageName,
-                                PackageManager.GET_PERMISSIONS, Process.myUid(),
-                                mContext.getUserId());
-                        boolean isSamplingTarget = isSamplingTarget(pi);
-                        int[] userIds = getUserManagerInternal().getUserIds();
-                        synchronized (AppOpsService.this) {
-                            if (isSamplingTarget) {
-                                mRarelyUsedPackages.add(packageName);
-                            }
-                            for (int i = 0; i < userIds.length; i++) {
-                                int uid = UserHandle.getUid(userIds[i], appId);
-                                UidState uidState = getUidStateLocked(uid, true);
-                                if (!uidState.pkgOps.containsKey(packageName)) {
-                                    uidState.pkgOps.put(packageName,
-                                            new Ops(packageName, uidState));
-                                }
-
-                                createSandboxUidStateIfNotExistsForAppLocked(uid);
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onPackageRemoved(String packageName, int appId) {
-                        int[] userIds = getUserManagerInternal().getUserIds();
-                        synchronized (AppOpsService.this) {
-                            for (int i = 0; i < userIds.length; i++) {
-                                int uid = UserHandle.getUid(userIds[i], appId);
-                                packageRemovedLocked(uid, packageName);
-                            }
-                        }
-                    }
-                });
     }
 
     /**
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index d5d8fd2..8fd2ee2 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -20,6 +20,7 @@
 // TODO(b/141025588): Create separate internal and external permissions for AuthService.
 // TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
 
+import static android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG;
 import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -304,6 +305,9 @@
             if (promptInfo.containsPrivateApiConfigurations()) {
                 checkInternalPermission();
             }
+            if (promptInfo.containsManageBioApiConfigurations()) {
+                checkManageBiometricPermission();
+            }
 
             final long identity = Binder.clearCallingIdentity();
             try {
@@ -984,6 +988,11 @@
                 "Must have USE_BIOMETRIC_INTERNAL permission");
     }
 
+    private void checkManageBiometricPermission() {
+        getContext().enforceCallingOrSelfPermission(MANAGE_BIOMETRIC_DIALOG,
+                "Must have MANAGE_BIOMETRIC_DIALOG permission");
+    }
+
     private void checkPermission() {
         if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
                 != PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/broadcastradio/TEST_MAPPING b/services/core/java/com/android/server/broadcastradio/TEST_MAPPING
new file mode 100644
index 0000000..ee4eeb6
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "frameworks/base/core/tests/BroadcastRadioTests"
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
index 1153cc3..8a3a56c 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.health;
 
+import static android.os.Flags.batteryPartStatusApi;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.health.BatteryHealthData;
@@ -150,6 +152,18 @@
                     healthData = service.getBatteryHealthData();
                     prop.setLong(healthData.batteryStateOfHealth);
                     break;
+                case BatteryManager.BATTERY_PROPERTY_SERIAL_NUMBER:
+                    if (batteryPartStatusApi()) {
+                        healthData = service.getBatteryHealthData();
+                        prop.setString(healthData.batterySerialNumber);
+                    }
+                    break;
+                case BatteryManager.BATTERY_PROPERTY_PART_STATUS:
+                    if (batteryPartStatusApi()) {
+                        healthData = service.getBatteryHealthData();
+                        prop.setLong(healthData.batteryPartStatus);
+                    }
+                    break;
             }
         } catch (UnsupportedOperationException e) {
             // Leave prop untouched.
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index 6236e2b..46668de 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -1370,7 +1370,7 @@
     public boolean isVirtualDevice(int deviceId) {
         VirtualDeviceManagerInternal vdm = LocalServices.getService(
                 VirtualDeviceManagerInternal.class);
-        return vdm == null || vdm.isInputDeviceOwnedByVirtualDevice(deviceId);
+        return vdm != null && vdm.isInputDeviceOwnedByVirtualDevice(deviceId);
     }
 
     private static int[] getScriptCodes(@Nullable Locale locale) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 4d19ead..d7188c7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -42,7 +42,6 @@
 import android.util.Log;
 import android.util.Slog;
 
-import com.android.internal.annotations.Keep;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.RingBuffer;
 import com.android.server.am.ProcessList;
@@ -414,7 +413,7 @@
         private static final Date sDate = new Date();
 
         public LogBuffer(int capacity) {
-            super(Data.class, capacity);
+            super(Data::new, Data[]::new, capacity);
         }
 
         public void uidStateChanged(int uid, int procState, long procStateSeq,
@@ -690,12 +689,8 @@
 
     /**
      * Container class for all networkpolicy events data.
-     *
-     * Note: This class needs to be public for RingBuffer class to be able to create
-     * new instances of this.
      */
-    @Keep
-    public static final class Data {
+    private static final class Data {
         public int type;
         public long timeStamp;
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c9f45e5..2ae040a6 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5931,8 +5931,7 @@
                         newVisualEffects, policy.priorityConversationSenders);
 
                 if (shouldApplyAsImplicitRule) {
-                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy,
-                            origin);
+                    mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, callingUid, policy);
                 } else {
                     ZenLog.traceSetNotificationPolicy(pkg, applicationInfo.targetSdkVersion,
                             policy);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index c7f5703..93ffd97 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -32,6 +32,7 @@
 import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
 
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
+import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
@@ -427,6 +428,7 @@
 
     public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
             @ConfigChangeOrigin int origin, String reason, int callingUid) {
+        requirePublicOrigin("addAutomaticZenRule", origin);
         if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
             PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
             if (component == null) {
@@ -525,6 +527,7 @@
 
     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
             @ConfigChangeOrigin int origin, String reason, int callingUid) {
+        requirePublicOrigin("updateAutomaticZenRule", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return false;
@@ -602,7 +605,11 @@
                     rule = newImplicitZenRule(callingPkg);
                     newConfig.automaticRules.put(rule.id, rule);
                 }
-                rule.zenMode = zenMode;
+                // If the user has changed the rule's *zenMode*, then don't let app overwrite it.
+                // We allow the update if the user has only changed other aspects of the rule.
+                if ((rule.userModifiedFields & AutomaticZenRule.FIELD_INTERRUPTION_FILTER) == 0) {
+                    rule.zenMode = zenMode;
+                }
                 rule.snoozing = false;
                 rule.condition = new Condition(rule.conditionId,
                         mContext.getString(R.string.zen_mode_implicit_activated),
@@ -625,7 +632,7 @@
      * {@link Global#ZEN_MODE_IMPORTANT_INTERRUPTIONS}.
      */
     void applyGlobalPolicyAsImplicitZenRule(String callingPkg, int callingUid,
-            NotificationManager.Policy policy, @ConfigChangeOrigin int origin) {
+            NotificationManager.Policy policy) {
         if (!android.app.Flags.modesApi()) {
             Log.wtf(TAG, "applyGlobalPolicyAsImplicitZenRule called with flag off!");
             return;
@@ -641,10 +648,17 @@
                 rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
                 newConfig.automaticRules.put(rule.id, rule);
             }
-            // TODO: b/308673679 - Keep user customization of this rule!
-            rule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
-            setConfigLocked(newConfig, /* triggeringComponent= */ null, origin,
-                    "applyGlobalPolicyAsImplicitZenRule", callingUid);
+            // If the user has changed the rule's *ZenPolicy*, then don't let app overwrite it.
+            // We allow the update if the user has only changed other aspects of the rule.
+            if (rule.zenPolicyUserModifiedFields == 0) {
+                updatePolicy(
+                        rule,
+                        ZenAdapters.notificationPolicyToZenPolicy(policy),
+                        /* updateBitmask= */ false);
+
+                setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
+                        "applyGlobalPolicyAsImplicitZenRule", callingUid);
+            }
         }
     }
 
@@ -726,6 +740,7 @@
 
     boolean removeAutomaticZenRule(String id, @ConfigChangeOrigin int origin, String reason,
             int callingUid) {
+        requirePublicOrigin("removeAutomaticZenRule", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return false;
@@ -758,6 +773,7 @@
 
     boolean removeAutomaticZenRules(String packageName, @ConfigChangeOrigin int origin,
             String reason, int callingUid) {
+        requirePublicOrigin("removeAutomaticZenRules", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return false;
@@ -806,6 +822,7 @@
 
     void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
             int callingUid) {
+        requirePublicOrigin("setAutomaticZenRuleState", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return;
@@ -819,6 +836,7 @@
 
     void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition,
             @ConfigChangeOrigin int origin, int callingUid) {
+        requirePublicOrigin("setAutomaticZenRuleState", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
             if (mConfig == null) return;
@@ -988,7 +1006,7 @@
         return null;
     }
 
-    void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+    private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
                          @ConfigChangeOrigin int origin, boolean isNew) {
         if (Flags.modesApi()) {
             // These values can always be edited by the app, so we apply changes immediately.
@@ -1053,11 +1071,9 @@
             rule.zenMode = newZenMode;
 
             // Updates the bitmask and values for all policy fields, based on the origin.
-            rule.zenPolicy = updatePolicy(rule.zenPolicy, automaticZenRule.getZenPolicy(),
-                    updateBitmask);
+            updatePolicy(rule, automaticZenRule.getZenPolicy(), updateBitmask);
             // Updates the bitmask and values for all device effect fields, based on the origin.
-            rule.zenDeviceEffects = updateZenDeviceEffects(
-                    rule.zenDeviceEffects, automaticZenRule.getDeviceEffects(),
+            updateZenDeviceEffects(rule, automaticZenRule.getDeviceEffects(),
                     origin == UPDATE_ORIGIN_APP, updateBitmask);
         } else {
             if (rule.enabled != automaticZenRule.isEnabled()) {
@@ -1069,13 +1085,6 @@
             rule.enabled = automaticZenRule.isEnabled();
             rule.modified = automaticZenRule.isModified();
             rule.zenPolicy = automaticZenRule.getZenPolicy();
-            if (Flags.modesApi()) {
-                rule.zenDeviceEffects = updateZenDeviceEffects(
-                        rule.zenDeviceEffects,
-                        automaticZenRule.getDeviceEffects(),
-                        origin == UPDATE_ORIGIN_APP,
-                        origin == UPDATE_ORIGIN_USER);
-            }
             rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                     automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
             rule.configurationActivity = automaticZenRule.getConfigurationActivity();
@@ -1099,28 +1108,28 @@
     }
 
     /**
-     * Modifies {@link ZenPolicy} that is being stored as part of a new or updated ZenRule.
-     * Returns a policy based on {@code oldPolicy}, but with fields updated to match
-     * {@code newPolicy} where they differ, and updating the internal user-modified bitmask to
-     * track these changes, if applicable based on {@code origin}.
+     * Modifies the {@link ZenPolicy} associated to a new or updated ZenRule.
+     *
+     * <p>The new policy is {@code newPolicy}, while the user-modified bitmask is updated to reflect
+     * the changes being applied (if applicable, i.e. if the update is from the user).
      */
-    @Nullable
-    private ZenPolicy updatePolicy(@Nullable ZenPolicy oldPolicy, @Nullable ZenPolicy newPolicy,
-                                   boolean updateBitmask) {
-        // If the update is to make the policy null, we don't need to update the bitmask,
-        // because it won't be stored anywhere anyway.
+    private void updatePolicy(ZenRule zenRule, @Nullable ZenPolicy newPolicy,
+            boolean updateBitmask) {
         if (newPolicy == null) {
-            return null;
+            // TODO: b/319242206 - Treat as newPolicy == default policy and continue below.
+            zenRule.zenPolicy = null;
+            return;
         }
 
         // If oldPolicy is null, we compare against the default policy when determining which
         // fields in the bitmask should be marked as updated.
-        if (oldPolicy == null) {
-            oldPolicy = mDefaultConfig.toZenPolicy();
-        }
+        ZenPolicy oldPolicy =
+                zenRule.zenPolicy != null ? zenRule.zenPolicy : mDefaultConfig.toZenPolicy();
 
-        int userModifiedFields = oldPolicy.getUserModifiedFields();
+        zenRule.zenPolicy = newPolicy;
+
         if (updateBitmask) {
+            int userModifiedFields = zenRule.zenPolicyUserModifiedFields;
             if (oldPolicy.getPriorityMessageSenders() != newPolicy.getPriorityMessageSenders()) {
                 userModifiedFields |= ZenPolicy.FIELD_MESSAGES;
             }
@@ -1178,66 +1187,47 @@
                     != newPolicy.getVisualEffectNotificationList()) {
                 userModifiedFields |= ZenPolicy.FIELD_VISUAL_EFFECT_NOTIFICATION_LIST;
             }
+            zenRule.zenPolicyUserModifiedFields = userModifiedFields;
         }
-
-        // After all bitmask changes have been made, sets the bitmask.
-        return new ZenPolicy.Builder(newPolicy).setUserModifiedFields(userModifiedFields).build();
     }
 
     /**
-     * Modifies {@link ZenDeviceEffects} that are being stored as part of a new or updated ZenRule.
-     * Returns a {@link ZenDeviceEffects} based on {@code oldEffects}, but with fields updated to
-     * match {@code newEffects} where they differ, and updating the internal user-modified bitmask
-     * to track these changes, if applicable based on {@code origin}.
-     * <ul>
-     *     <li> Apps cannot turn on hidden effects (those tagged as {@code @hide}) since they are
-     *     intended for platform-specific rules (e.g. wearables). If it's a new rule, we blank them
-     *     out; if it's an update, we preserve the previous values.
-     * </ul>
+     * Modifies the {@link ZenDeviceEffects} associated to a new or updated ZenRule.
+     *
+     * <p>The new value is {@code newEffects}, while the user-modified bitmask is updated to reflect
+     * the changes being applied (if applicable, i.e. if the update is from the user).
+     *
+     * <p>Apps cannot turn on hidden effects (those tagged as {@code @hide}), so those fields are
+     * treated especially: for a new rule, they are blanked out; for an updated rule, previous
+     * values are preserved.
      */
-    @Nullable
-    private static ZenDeviceEffects updateZenDeviceEffects(@Nullable ZenDeviceEffects oldEffects,
-                                                           @Nullable ZenDeviceEffects newEffects,
-                                                           boolean isFromApp,
-                                                           boolean updateBitmask) {
+    private static void updateZenDeviceEffects(ZenRule zenRule,
+            @Nullable ZenDeviceEffects newEffects, boolean isFromApp, boolean updateBitmask) {
         if (newEffects == null) {
-            return null;
+            zenRule.zenDeviceEffects = null;
+            return;
         }
 
-        // Since newEffects is not null, we want to adopt all the new provided device effects.
-        ZenDeviceEffects.Builder builder = new ZenDeviceEffects.Builder(newEffects);
+        ZenDeviceEffects oldEffects = zenRule.zenDeviceEffects != null
+                ? zenRule.zenDeviceEffects
+                : new ZenDeviceEffects.Builder().build();
 
         if (isFromApp) {
-            if (oldEffects != null) {
-                // We can do this because we know we don't need to update the bitmask FROM_APP.
-                return builder
-                        .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
-                        .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
-                        .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
-                        .setShouldDisableTouch(oldEffects.shouldDisableTouch())
-                        .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
-                        .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
-                        .build();
-            } else {
-                return builder
-                        .setShouldDisableAutoBrightness(false)
-                        .setShouldDisableTapToWake(false)
-                        .setShouldDisableTiltToWake(false)
-                        .setShouldDisableTouch(false)
-                        .setShouldMinimizeRadioUsage(false)
-                        .setShouldMaximizeDoze(false)
-                        .build();
-            }
+            // Don't allow apps to toggle hidden effects.
+            newEffects = new ZenDeviceEffects.Builder(newEffects)
+                    .setShouldDisableAutoBrightness(oldEffects.shouldDisableAutoBrightness())
+                    .setShouldDisableTapToWake(oldEffects.shouldDisableTapToWake())
+                    .setShouldDisableTiltToWake(oldEffects.shouldDisableTiltToWake())
+                    .setShouldDisableTouch(oldEffects.shouldDisableTouch())
+                    .setShouldMinimizeRadioUsage(oldEffects.shouldMinimizeRadioUsage())
+                    .setShouldMaximizeDoze(oldEffects.shouldMaximizeDoze())
+                    .build();
         }
 
-        // If oldEffects is null, we compare against the default device effects object when
-        // determining which fields in the bitmask should be marked as updated.
-        if (oldEffects == null) {
-            oldEffects = new ZenDeviceEffects.Builder().build();
-        }
+        zenRule.zenDeviceEffects = newEffects;
 
-        int userModifiedFields = oldEffects.getUserModifiedFields();
         if (updateBitmask) {
+            int userModifiedFields = zenRule.zenDeviceEffectsUserModifiedFields;
             if (oldEffects.shouldDisplayGrayscale() != newEffects.shouldDisplayGrayscale()) {
                 userModifiedFields |= ZenDeviceEffects.FIELD_GRAYSCALE;
             }
@@ -1270,11 +1260,8 @@
             if (oldEffects.shouldMaximizeDoze() != newEffects.shouldMaximizeDoze()) {
                 userModifiedFields |= ZenDeviceEffects.FIELD_MAXIMIZE_DOZE;
             }
+            zenRule.zenDeviceEffectsUserModifiedFields = userModifiedFields;
         }
-
-        // Since newEffects is not null, we want to adopt all the new provided device effects.
-        // Set the usermodifiedFields value separately, to reflect the updated bitmask.
-        return builder.setUserModifiedFields(userModifiedFields).build();
     }
 
     private AutomaticZenRule zenRuleToAutomaticZenRule(ZenRule rule) {
@@ -1293,7 +1280,6 @@
                     .setOwner(rule.component)
                     .setConfigurationActivity(rule.configurationActivity)
                     .setTriggerDescription(rule.triggerDescription)
-                    .setUserModifiedFields(rule.userModifiedFields)
                     .build();
         } else {
             azr = new AutomaticZenRule(rule.name, rule.component,
@@ -2369,6 +2355,19 @@
             return null;
         }
     }
+
+    /** Checks that the {@code origin} supplied to a ZenModeHelper "API" method makes sense. */
+    private static void requirePublicOrigin(String method, @ConfigChangeOrigin int origin) {
+        if (!Flags.modesApi()) {
+            return;
+        }
+        checkArgument(origin == UPDATE_ORIGIN_APP || origin == UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI
+                        || origin == UPDATE_ORIGIN_USER,
+                "Expected one of UPDATE_ORIGIN_APP, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, or "
+                        + "UPDATE_ORIGIN_USER for %s, but received '%s'.",
+                method, origin);
+    }
+
     private final class Metrics extends Callback {
         private static final String COUNTER_MODE_PREFIX = "dnd_mode_";
         private static final String COUNTER_TYPE_PREFIX = "dnd_type_";
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index 3e5759a..b18f2bf 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -51,6 +51,7 @@
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ArchivedActivityParcel;
+import android.content.pm.ArchivedPackageInfo;
 import android.content.pm.ArchivedPackageParcel;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
@@ -402,23 +403,30 @@
                 installerPackage, /* flags= */ 0, userId);
         if (installerInfo == null) {
             // Should never happen because we just fetched the installerInfo.
-            Slog.e(TAG, "Couldnt find installer " + installerPackage);
+            Slog.e(TAG, "Couldn't find installer " + installerPackage);
             return null;
         }
+        final int iconSize = mContext.getSystemService(
+                ActivityManager.class).getLauncherLargeIconSize();
+
+        var info = new ArchivedPackageInfo(archivedPackage);
 
         try {
-            var packageName = archivedPackage.packageName;
-            var mainActivities = archivedPackage.archivedActivities;
-            List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.length);
-            for (int i = 0, size = mainActivities.length; i < size; ++i) {
-                var mainActivity = mainActivities[i];
-                Path iconPath = storeIconForParcel(packageName, mainActivity, userId, i);
+            var packageName = info.getPackageName();
+            var mainActivities = info.getLauncherActivities();
+            List<ArchiveActivityInfo> archiveActivityInfos = new ArrayList<>(mainActivities.size());
+            for (int i = 0, size = mainActivities.size(); i < size; ++i) {
+                var mainActivity = mainActivities.get(i);
+                Path iconPath = storeDrawable(
+                        packageName, mainActivity.getIcon(), userId, i, iconSize);
+                Path monochromePath =  storeDrawable(
+                        packageName, mainActivity.getMonochromeIcon(), userId, i, iconSize);
                 ArchiveActivityInfo activityInfo =
                         new ArchiveActivityInfo(
-                                mainActivity.title,
-                                mainActivity.originalComponentName,
+                                mainActivity.getLabel().toString(),
+                                mainActivity.getComponentName(),
                                 iconPath,
-                                null);
+                                monochromePath);
                 archiveActivityInfos.add(activityInfo);
             }
 
@@ -452,21 +460,6 @@
         return new ArchiveState(archiveActivityInfos, installerTitle);
     }
 
-    // TODO(b/298452477) Handle monochrome icons.
-    private static Path storeIconForParcel(String packageName, ArchivedActivityParcel mainActivity,
-            @UserIdInt int userId, int index) throws IOException {
-        if (mainActivity.iconBitmap == null) {
-            return null;
-        }
-        File iconsDir = createIconsDir(packageName, userId);
-        File iconFile = new File(iconsDir, index + ".png");
-        try (FileOutputStream out = new FileOutputStream(iconFile)) {
-            out.write(mainActivity.iconBitmap);
-            out.flush();
-        }
-        return iconFile.toPath();
-    }
-
     @VisibleForTesting
     Path storeIcon(String packageName, LauncherActivityInfo mainActivity,
             @UserIdInt int userId, int index, int iconSize) throws IOException {
@@ -475,9 +468,18 @@
             // The app doesn't define an icon. No need to store anything.
             return null;
         }
+        return storeDrawable(packageName, mainActivity.getIcon(/* density= */ 0), userId, index,
+                iconSize);
+    }
+
+    private static Path storeDrawable(String packageName, @Nullable Drawable iconDrawable,
+            @UserIdInt int userId, int index, int iconSize) throws IOException {
+        if (iconDrawable == null) {
+            return null;
+        }
         File iconsDir = createIconsDir(packageName, userId);
         File iconFile = new File(iconsDir, index + ".png");
-        Bitmap icon = drawableToBitmap(mainActivity.getIcon(/* density= */ 0), iconSize);
+        Bitmap icon = drawableToBitmap(iconDrawable, iconSize);
         try (FileOutputStream out = new FileOutputStream(iconFile)) {
             // Note: Quality is ignored for PNGs.
             if (!icon.compress(Bitmap.CompressFormat.PNG, /* quality= */ 100, out)) {
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 3e3b72c..3659cb7 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -485,8 +485,6 @@
         synchronized (mPm.mInstallLock) {
             cleanUpResourcesLI(codeFile, instructionSets);
         }
-        // TODO: open logging to help debug, will delete or add debug flag
-        Slog.d(TAG, "cleanUpResources for " + codeFile);
         if (packageName == null) {
             return;
         }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index e993d9e..d644235 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -33,6 +33,7 @@
 import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.SetSchemaRequest;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -47,6 +48,7 @@
 import android.os.Binder;
 import android.os.PersistableBundle;
 import android.os.StrictMode;
+import android.os.SystemClock;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -192,6 +194,9 @@
     private long mLastKnownForegroundElapsedTime;
 
     @GuardedBy("mLock")
+    private long mLastReportedTime;
+
+    @GuardedBy("mLock")
     private boolean mIsAppSearchSchemaUpToDate;
 
     private ShortcutPackage(ShortcutUser shortcutUser,
@@ -1673,6 +1678,26 @@
         return condition[0];
     }
 
+    void reportShortcutUsed(@NonNull final UsageStatsManagerInternal usageStatsManagerInternal,
+            @NonNull final String shortcutId) {
+        synchronized (mLock) {
+            final long currentTS = SystemClock.elapsedRealtime();
+            final ShortcutService s = mShortcutUser.mService;
+            if (currentTS - mLastReportedTime > s.mSaveDelayMillis) {
+                mLastReportedTime = currentTS;
+            } else {
+                return;
+            }
+            final long token = s.injectClearCallingIdentity();
+            try {
+                usageStatsManagerInternal.reportShortcutUsage(getPackageName(), shortcutId,
+                        getPackageUserId());
+            } finally {
+                s.injectRestoreCallingIdentity(token);
+            }
+        }
+    }
+
     public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) {
         pw.println();
 
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 96c205c..c23d2ab 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -371,7 +371,7 @@
     private CompressFormat mIconPersistFormat;
     private int mIconPersistQuality;
 
-    private int mSaveDelayMillis;
+    int mSaveDelayMillis;
 
     private final IPackageManager mIPackageManager;
     private final PackageManagerInternal mPackageManagerInternal;
@@ -2291,7 +2291,7 @@
 
         packageShortcutsChanged(ps, changedShortcuts, removedShortcuts);
 
-        reportShortcutUsedInternal(packageName, shortcut.getId(), userId);
+        ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcut.getId());
 
         verifyStates();
     }
@@ -2695,25 +2695,17 @@
             Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
                     shortcutId, packageName, userId));
         }
+        final ShortcutPackage ps;
         synchronized (mLock) {
             throwIfUserLockedL(userId);
-            final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+            ps = getPackageShortcutsForPublisherLocked(packageName, userId);
             if (ps.findShortcutById(shortcutId) == null) {
                 Log.w(TAG, String.format("reportShortcutUsed: package %s doesn't have shortcut %s",
                         packageName, shortcutId));
                 return;
             }
         }
-        reportShortcutUsedInternal(packageName, shortcutId, userId);
-    }
-
-    private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) {
-        final long token = injectClearCallingIdentity();
-        try {
-            mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
-        } finally {
-            injectRestoreCallingIdentity(token);
-        }
+        ps.reportShortcutUsed(mUsageStatsManagerInternal, shortcutId);
     }
 
     @Override
@@ -5202,13 +5194,11 @@
     }
 
     // Injection point.
-    @VisibleForTesting
     long injectClearCallingIdentity() {
         return Binder.clearCallingIdentity();
     }
 
     // Injection point.
-    @VisibleForTesting
     void injectRestoreCallingIdentity(long token) {
         Binder.restoreCallingIdentity(token);
     }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c1b7489..7b0a69b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -46,6 +46,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
@@ -587,7 +588,10 @@
         public void onFinished(int id, Bundle extras) {
             mHandler.post(() -> {
                 try {
-                    mContext.startIntentSender(mTarget, null, 0, 0, 0);
+                    ActivityOptions activityOptions =
+                            ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+                                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+                    mContext.startIntentSender(mTarget, null, 0, 0, 0, activityOptions.toBundle());
                 } catch (IntentSender.SendIntentException e) {
                     Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
                 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index e226953..a172de0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -4413,8 +4413,8 @@
 
     private boolean setPowerModeInternal(int mode, boolean enabled) {
         // Maybe filter the event.
-        if (mBatterySaverStateMachine == null || (mode == Mode.LAUNCH && enabled
-                && mBatterySaverStateMachine.getBatterySaverController().isLaunchBoostDisabled())) {
+        if (mode == Mode.LAUNCH && enabled && mBatterySaverStateMachine != null
+                && mBatterySaverStateMachine.getBatterySaverController().isLaunchBoostDisabled()) {
             return false;
         }
         return mNativeWrapper.nativeSetPowerMode(mode, enabled);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 60dc4ff..d95b431 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -348,6 +348,10 @@
 
     private void pinWebviewIfRequired(ApplicationInfo appInfo) {
         PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+        if (pinnerService == null) {
+            // This happens in unit tests which do not have services.
+            return;
+        }
         int webviewPinQuota = pinnerService.getWebviewPinQuota();
         if (webviewPinQuota <= 0) {
             return;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 676203b..2e0546e 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -778,17 +778,22 @@
         try {
             synchronized (mGlobalLock) {
                 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+                if (r == null) {
+                    return false;
+                }
                 // Create a transition if the activity is playing in case the below activity didn't
                 // commit invisible. That's because if any activity below this one has changed its
                 // visibility while playing transition, there won't able to commit visibility until
                 // the running transition finish.
-                final Transition transition = r != null
-                        && r.mTransitionController.inPlayingTransition(r)
+                final Transition transition = r.mTransitionController.isShellTransitionsEnabled()
                         && !r.mTransitionController.isCollecting()
                         ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null;
-                final boolean changed = r != null && r.setOccludesParent(true);
+                final boolean changed = r.setOccludesParent(true);
                 if (transition != null) {
                     if (changed) {
+                        // Always set as scene transition because it expects to be a jump-cut.
+                        transition.setOverrideAnimation(TransitionInfo.AnimationOptions
+                                .makeSceneTransitionAnimOptions(), null, null);
                         r.mTransitionController.requestStartTransition(transition,
                                 null /*startTask */, null /* remoteTransition */,
                                 null /* displayChange */);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 69fbe6b..9b1f9c8 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3093,7 +3093,6 @@
         final boolean changed = occludesParent != mOccludesParent;
         mOccludesParent = occludesParent;
         setMainWindowOpaque(occludesParent);
-        mWmService.mWindowPlacerLocked.requestTraversal();
 
         if (changed && task != null && !occludesParent) {
             getRootTask().convertActivityToTranslucent(this);
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index e7621ff..182e1c1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
 import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
@@ -144,22 +145,20 @@
     }
 
     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
-        Bundle bOptions = deferCrossProfileAppsAnimationIfNecessary();
+        ActivityOptions activityOptions = deferCrossProfileAppsAnimationIfNecessary();
+        activityOptions.setPendingIntentCreatorBackgroundActivityStartMode(
+                MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         final TaskFragment taskFragment = getLaunchTaskFragment();
         // If the original intent is going to be embedded, try to forward the embedding TaskFragment
         // and its task id to embed back the original intent.
         if (taskFragment != null) {
-            ActivityOptions activityOptions = bOptions != null
-                    ? ActivityOptions.fromBundle(bOptions)
-                    : ActivityOptions.makeBasic();
             activityOptions.setLaunchTaskFragmentToken(taskFragment.getFragmentToken());
-            bOptions = activityOptions.toBundle();
         }
         final IIntentSender target = mService.getIntentSenderLocked(
                 INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId,
                 null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
                 new Intent[] { mIntent }, new String[] { mResolvedType },
-                flags, bOptions);
+                flags, activityOptions.toBundle());
         return new IntentSender(target);
     }
 
@@ -272,12 +271,12 @@
      *
      * @return the activity option used to start the original intent.
      */
-    private Bundle deferCrossProfileAppsAnimationIfNecessary() {
+    private ActivityOptions deferCrossProfileAppsAnimationIfNecessary() {
         if (hasCrossProfileAnimation()) {
             mActivityOptions = null;
-            return ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle();
+            return ActivityOptions.makeOpenCrossProfileAppsAnimation();
         }
-        return null;
+        return ActivityOptions.makeBasic();
     }
 
     private boolean interceptQuietProfileIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f620a97..2accf9a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2841,6 +2841,19 @@
         }
     }
 
+    /** Returns {@code true} if the display should use high performance hint for this transition. */
+    boolean shouldUsePerfHint(@NonNull DisplayContent dc) {
+        if (mOverrideOptions != null
+                && mOverrideOptions.getType() == ActivityOptions.ANIM_SCENE_TRANSITION
+                && mType == TRANSIT_TO_BACK && mParticipants.size() == 1) {
+            // This should be from convertFromTranslucent that makes the occluded activity invisible
+            // without animation. So do not use perf hint (especially early-wakeup) that may disturb
+            // SurfaceFlinger scheduling around the last frame.
+            return false;
+        }
+        return mTargetDisplays.contains(dc);
+    }
+
     /**
      * Returns {@code true} if the transition and the corresponding transaction should be applied
      * on display thread. Currently, this only checks for display rotation change because the order
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 708d63e..59e3350 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -1237,8 +1237,15 @@
             // enableHighPerfTransition(true) is also called in Transition#recordDisplay.
             for (int i = mAtm.mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
                 final DisplayContent dc = mAtm.mRootWindowContainer.getChildAt(i);
-                if (isTransitionOnDisplay(dc)) {
+                if (mCollectingTransition != null && mCollectingTransition.shouldUsePerfHint(dc)) {
                     dc.enableHighPerfTransition(true);
+                    continue;
+                }
+                for (int j = mPlayingTransitions.size() - 1; j >= 0; j--) {
+                    if (mPlayingTransitions.get(j).shouldUsePerfHint(dc)) {
+                        dc.enableHighPerfTransition(true);
+                        break;
+                    }
                 }
             }
             // Usually transitions put quite a load onto the system already (with all the things
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
index 94caf28..c8a6545 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppIdAppOpPolicy.kt
@@ -17,6 +17,7 @@
 package com.android.server.permission.access.appop
 
 import android.app.AppOpsManager
+import android.util.Slog
 import com.android.server.permission.access.GetStateScope
 import com.android.server.permission.access.MutableAccessState
 import com.android.server.permission.access.MutateStateScope
@@ -84,6 +85,10 @@
         appOpName: String,
         mode: Int
     ): Boolean {
+        if (userId !in newState.userStates) {
+            Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId")
+            return false
+        }
         val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
         val oldMode =
             newState.userStates[userId]!!
@@ -152,4 +157,8 @@
          */
         abstract fun onStateMutated()
     }
+
+    companion object {
+        private val LOG_TAG = AppIdAppOpPolicy::class.java.simpleName
+    }
 }
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index 8f464d4..3ee7430 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -17,29 +17,62 @@
 package com.android.server.permission.access.appop
 
 import android.app.AppOpsManager
+import android.os.Binder
 import android.os.Handler
 import android.os.UserHandle
+import android.permission.flags.Flags
 import android.util.ArrayMap
 import android.util.ArraySet
+import android.util.LongSparseArray
+import android.util.Slog
+import android.util.SparseArray
 import android.util.SparseBooleanArray
 import android.util.SparseIntArray
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.util.IntPair
 import com.android.server.appop.AppOpsCheckingServiceInterface
 import com.android.server.appop.AppOpsCheckingServiceInterface.AppOpsModeChangedListener
 import com.android.server.permission.access.AccessCheckingService
 import com.android.server.permission.access.AppOpUri
+import com.android.server.permission.access.GetStateScope
 import com.android.server.permission.access.PackageUri
+import com.android.server.permission.access.PermissionUri
 import com.android.server.permission.access.UidUri
+import com.android.server.permission.access.appop.AppOpModes.MODE_ALLOWED
+import com.android.server.permission.access.appop.AppOpModes.MODE_FOREGROUND
+import com.android.server.permission.access.appop.AppOpModes.MODE_IGNORED
 import com.android.server.permission.access.collection.forEachIndexed
 import com.android.server.permission.access.collection.set
+import com.android.server.permission.access.permission.AppIdPermissionPolicy
+import com.android.server.permission.access.permission.PermissionFlags
+import com.android.server.permission.access.permission.PermissionService
 
 class AppOpService(private val service: AccessCheckingService) : AppOpsCheckingServiceInterface {
     private val packagePolicy =
         service.getSchemePolicy(PackageUri.SCHEME, AppOpUri.SCHEME) as PackageAppOpPolicy
     private val appIdPolicy =
         service.getSchemePolicy(UidUri.SCHEME, AppOpUri.SCHEME) as AppIdAppOpPolicy
+    private val permissionPolicy =
+        service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
 
     private val context = service.context
+
+    // Maps appop code to its runtime permission
+    private val runtimeAppOpToPermissionNames = SparseArray<String>()
+
+    // Maps runtime permission to its appop codes
+    private val runtimePermissionNameToAppOp = ArrayMap<String, Int>()
+
+    private var foregroundableOps = SparseBooleanArray()
+
+    /* Maps foreground permissions to their background permission. Background permissions aren't
+    required to be runtime */
+    private val foregroundToBackgroundPermissionName = ArrayMap<String, String>()
+
+    /* Maps background permissions to their foreground permissions. Background permissions aren't
+    required to be runtime */
+    private val backgroundToForegroundPermissionNames = ArrayMap<String, ArraySet<String>>()
+
     private lateinit var handler: Handler
 
     @Volatile private var listeners = ArraySet<AppOpsModeChangedListener>()
@@ -68,11 +101,58 @@
     }
 
     override fun systemReady() {
-        // Not implemented because upgrades are handled automatically.
+        if (useRuntimePermissionAppOpMapping()) {
+            createPermissionAppOpMapping()
+            permissionPolicy.addOnPermissionFlagsChangedListener(OnPermissionFlagsChangedListener())
+        }
+    }
+
+    private fun createPermissionAppOpMapping() {
+        val permissions = service.getState { with(permissionPolicy) { getPermissions() } }
+
+        for (appOpCode in 0 until AppOpsManager._NUM_OP) {
+            AppOpsManager.opToPermission(appOpCode)?.let { permissionName ->
+                // Multiple ops might map to a single permission but only one is considered the
+                // runtime appop calculations.
+                if (appOpCode == AppOpsManager.permissionToOpCode(permissionName)) {
+                    val permission = permissions[permissionName]!!
+                    if (permission.isRuntime) {
+                        runtimePermissionNameToAppOp[permissionName] = appOpCode
+                        runtimeAppOpToPermissionNames[appOpCode] = permissionName
+                        permission.permissionInfo.backgroundPermission?.let {
+                            backgroundPermissionName ->
+                            // Note: background permission may not be runtime,
+                            // e.g. microphone/camera.
+                            foregroundableOps[appOpCode] = true
+                            foregroundToBackgroundPermissionName[permissionName] =
+                                backgroundPermissionName
+                            backgroundToForegroundPermissionNames
+                                .getOrPut(backgroundPermissionName, ::ArraySet)
+                                .add(permissionName)
+                        }
+                    }
+                }
+            }
+        }
     }
 
     override fun getNonDefaultUidModes(uid: Int, persistentDeviceId: String): SparseIntArray {
-        return opNameMapToOpSparseArray(getUidModes(uid))
+        val appId = UserHandle.getAppId(uid)
+        val userId = UserHandle.getUserId(uid)
+        service.getState {
+            val modes =
+                with(appIdPolicy) { opNameMapToOpSparseArray(getAppOpModes(appId, userId)?.map) }
+            if (useRuntimePermissionAppOpMapping()) {
+                runtimePermissionNameToAppOp.forEachIndexed { _, permissionName, appOpCode ->
+                    val mode = getUidModeFromPermissionState(appId, userId, permissionName)
+                    if (mode != AppOpsManager.opToDefaultMode(appOpCode)) {
+                        modes[appOpCode] = mode
+                    }
+                }
+            }
+
+            return modes
+        }
     }
 
     override fun getNonDefaultPackageModes(packageName: String, userId: Int): SparseIntArray {
@@ -83,7 +163,13 @@
         val appId = UserHandle.getAppId(uid)
         val userId = UserHandle.getUserId(uid)
         val opName = AppOpsManager.opToPublicName(op)
-        return service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
+        val permissionName = runtimeAppOpToPermissionNames[op]
+
+        return if (!useRuntimePermissionAppOpMapping() || permissionName == null) {
+            service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, opName) } }
+        } else {
+            service.getState { getUidModeFromPermissionState(appId, userId, permissionName) }
+        }
     }
 
     private fun getUidModes(uid: Int): ArrayMap<String, Int>? {
@@ -92,13 +178,63 @@
         return service.getState { with(appIdPolicy) { getAppOpModes(appId, userId) } }?.map
     }
 
-    override fun setUidMode(uid: Int, persistentDeviceId: String, op: Int, mode: Int): Boolean {
+    private fun GetStateScope.getUidModeFromPermissionState(
+        appId: Int,
+        userId: Int,
+        permissionName: String
+    ): Int {
+        val permissionFlags =
+            with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
+        val backgroundPermissionName = foregroundToBackgroundPermissionName[permissionName]
+        val backgroundPermissionFlags =
+            if (backgroundPermissionName != null) {
+                with(permissionPolicy) {
+                    getPermissionFlags(appId, userId, backgroundPermissionName)
+                }
+            } else {
+                PermissionFlags.RUNTIME_GRANTED
+            }
+        val result = evaluateModeFromPermissionFlags(permissionFlags, backgroundPermissionFlags)
+        if (result != MODE_IGNORED) {
+            return result
+        }
+
+        val fullerPermissionName =
+            PermissionService.getFullerPermission(permissionName) ?: return result
+        return getUidModeFromPermissionState(appId, userId, fullerPermissionName)
+    }
+
+    private fun evaluateModeFromPermissionFlags(
+        foregroundFlags: Int,
+        backgroundFlags: Int = PermissionFlags.RUNTIME_GRANTED
+    ): Int =
+        if (PermissionFlags.isAppOpGranted(foregroundFlags)) {
+            if (PermissionFlags.isAppOpGranted(backgroundFlags)) {
+                MODE_ALLOWED
+            } else {
+                MODE_FOREGROUND
+            }
+        } else {
+            MODE_IGNORED
+        }
+
+    override fun setUidMode(uid: Int, persistentDeviceId: String, code: Int, mode: Int): Boolean {
+        if (useRuntimePermissionAppOpMapping() && code in runtimeAppOpToPermissionNames) {
+            Slog.w(
+                LOG_TAG,
+                "Cannot set UID mode for runtime permission app op, uid = $uid," +
+                    " code = ${AppOpsManager.opToName(code)}, mode = ${AppOpsManager.modeToName(mode)}",
+                RuntimeException()
+            )
+            return false
+        }
+
         val appId = UserHandle.getAppId(uid)
         val userId = UserHandle.getUserId(uid)
-        val opName = AppOpsManager.opToPublicName(op)
-        var wasChanged = false
+        val appOpName = AppOpsManager.opToPublicName(code)
+        var wasChanged: Boolean
         service.mutateState {
-            wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, opName, mode) }
+            wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) }
         }
         return wasChanged
     }
@@ -113,10 +249,22 @@
     private fun getPackageModes(packageName: String, userId: Int): ArrayMap<String, Int>? =
         service.getState { with(packagePolicy) { getAppOpModes(packageName, userId) } }?.map
 
-    override fun setPackageMode(packageName: String, op: Int, mode: Int, userId: Int) {
-        val opName = AppOpsManager.opToPublicName(op)
+    override fun setPackageMode(packageName: String, appOpCode: Int, mode: Int, userId: Int) {
+        val appOpName = AppOpsManager.opToPublicName(appOpCode)
+
+        if (
+            useRuntimePermissionAppOpMapping() && runtimeAppOpToPermissionNames.contains(appOpCode)
+        ) {
+            Slog.w(
+                LOG_TAG,
+                "(packageName=$packageName, userId=$userId)'s appop state" +
+                    " for runtime op $appOpName should not be set directly.",
+                RuntimeException()
+            )
+            return
+        }
         service.mutateState {
-            with(packagePolicy) { setAppOpMode(packageName, userId, opName, mode) }
+            with(packagePolicy) { setAppOpMode(packageName, userId, appOpName, mode) }
         }
     }
 
@@ -127,7 +275,7 @@
     }
 
     override fun removePackage(packageName: String, userId: Int): Boolean {
-        var wasChanged = false
+        var wasChanged: Boolean
         service.mutateState {
             wasChanged = with(packagePolicy) { removeAppOpModes(packageName, userId) }
         }
@@ -157,6 +305,13 @@
                     this[AppOpsManager.strOpToOp(op)] = true
                 }
             }
+            if (useRuntimePermissionAppOpMapping()) {
+                foregroundableOps.forEachIndexed { _, op, _ ->
+                    if (getUidMode(uid, persistentDeviceId, op) == AppOpsManager.MODE_FOREGROUND) {
+                        this[op] = true
+                    }
+                }
+            }
         }
     }
 
@@ -167,6 +322,13 @@
                     this[AppOpsManager.strOpToOp(op)] = true
                 }
             }
+            if (useRuntimePermissionAppOpMapping()) {
+                foregroundableOps.forEachIndexed { _, op, _ ->
+                    if (getPackageMode(packageName, op, userId) == AppOpsManager.MODE_FOREGROUND) {
+                        this[op] = true
+                    }
+                }
+            }
         }
     }
 
@@ -188,9 +350,10 @@
         }
     }
 
-    inner class OnAppIdAppOpModeChangedListener : AppIdAppOpPolicy.OnAppOpModeChangedListener() {
+    private inner class OnAppIdAppOpModeChangedListener :
+        AppIdAppOpPolicy.OnAppOpModeChangedListener() {
         // (uid, appOpCode) -> newMode
-        val pendingChanges = ArrayMap<Pair<Int, Int>, Int>()
+        private val pendingChanges = LongSparseArray<Int>()
 
         override fun onAppOpModeChanged(
             appId: Int,
@@ -201,7 +364,7 @@
         ) {
             val uid = UserHandle.getUid(userId, appId)
             val appOpCode = AppOpsManager.strOpToOp(appOpName)
-            val key = Pair(uid, appOpCode)
+            val key = IntPair.of(uid, appOpCode)
 
             pendingChanges[key] = newMode
         }
@@ -210,8 +373,8 @@
             val listenersLocal = listeners
             pendingChanges.forEachIndexed { _, key, mode ->
                 listenersLocal.forEachIndexed { _, listener ->
-                    val uid = key.first
-                    val appOpCode = key.second
+                    val uid = IntPair.first(key)
+                    val appOpCode = IntPair.second(key)
 
                     listener.onUidModeChanged(uid, appOpCode, mode)
                 }
@@ -224,7 +387,7 @@
     private inner class OnPackageAppOpModeChangedListener :
         PackageAppOpPolicy.OnAppOpModeChangedListener() {
         // (packageName, userId, appOpCode) -> newMode
-        val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
+        private val pendingChanges = ArrayMap<Triple<String, Int, Int>, Int>()
 
         override fun onAppOpModeChanged(
             packageName: String,
@@ -254,4 +417,115 @@
             pendingChanges.clear()
         }
     }
+
+    private inner class OnPermissionFlagsChangedListener :
+        AppIdPermissionPolicy.OnPermissionFlagsChangedListener {
+        // (uid, appOpCode) -> newMode
+        private val pendingChanges = LongSparseArray<Int>()
+
+        override fun onPermissionFlagsChanged(
+            appId: Int,
+            userId: Int,
+            permissionName: String,
+            oldFlags: Int,
+            newFlags: Int
+        ) {
+            backgroundToForegroundPermissionNames[permissionName]?.let { foregroundPermissions ->
+                // This is a background permission; there may be multiple foreground permissions
+                // affected.
+                foregroundPermissions.forEachIndexed { _, foregroundPermissionName ->
+                    runtimePermissionNameToAppOp[foregroundPermissionName]?.let { appOpCode ->
+                        val foregroundPermissionFlags =
+                            getPermissionFlags(appId, userId, foregroundPermissionName)
+                        addPendingChangedModeIfNeeded(
+                            appId,
+                            userId,
+                            appOpCode,
+                            foregroundPermissionFlags,
+                            oldFlags,
+                            foregroundPermissionFlags,
+                            newFlags
+                        )
+                    }
+                }
+            }
+                ?: foregroundToBackgroundPermissionName[permissionName]?.let { backgroundPermission
+                    ->
+                    runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
+                        val backgroundPermissionFlags =
+                            getPermissionFlags(appId, userId, backgroundPermission)
+                        addPendingChangedModeIfNeeded(
+                            appId,
+                            userId,
+                            appOpCode,
+                            oldFlags,
+                            backgroundPermissionFlags,
+                            newFlags,
+                            backgroundPermissionFlags
+                        )
+                    }
+                }
+                    ?: runtimePermissionNameToAppOp[permissionName]?.let { appOpCode ->
+                    addPendingChangedModeIfNeeded(
+                        appId,
+                        userId,
+                        appOpCode,
+                        oldFlags,
+                        PermissionFlags.RUNTIME_GRANTED,
+                        newFlags,
+                        PermissionFlags.RUNTIME_GRANTED
+                    )
+                }
+        }
+
+        private fun getPermissionFlags(appId: Int, userId: Int, permissionName: String): Int =
+            service.getState {
+                with(permissionPolicy) { getPermissionFlags(appId, userId, permissionName) }
+            }
+
+        private fun addPendingChangedModeIfNeeded(
+            appId: Int,
+            userId: Int,
+            appOpCode: Int,
+            oldForegroundFlags: Int,
+            oldBackgroundFlags: Int,
+            newForegroundFlags: Int,
+            newBackgroundFlags: Int,
+        ) {
+            val oldMode = evaluateModeFromPermissionFlags(oldForegroundFlags, oldBackgroundFlags)
+            val newMode = evaluateModeFromPermissionFlags(newForegroundFlags, newBackgroundFlags)
+
+            if (oldMode != newMode) {
+                val uid = UserHandle.getUid(userId, appId)
+                pendingChanges[IntPair.of(uid, appOpCode)] = newMode
+            }
+        }
+
+        override fun onStateMutated() {
+            val listenersLocal = listeners
+            pendingChanges.forEachIndexed { _, key, mode ->
+                listenersLocal.forEachIndexed { _, listener ->
+                    val uid = IntPair.first(key)
+                    val appOpCode = IntPair.second(key)
+
+                    listener.onUidModeChanged(uid, appOpCode, mode)
+                }
+            }
+
+            pendingChanges.clear()
+        }
+    }
+
+    companion object {
+        private val LOG_TAG = AppOpService::class.java.simpleName
+
+        private fun useRuntimePermissionAppOpMapping(): Boolean {
+            val token = Binder.clearCallingIdentity()
+            return try {
+                Flags.runtimePermissionAppopsMapping()
+            } finally {
+                Binder.restoreCallingIdentity(token)
+            }
+        }
+    }
 }
diff --git a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
index 0d9470e..2f15dc7 100644
--- a/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/PackageAppOpPolicy.kt
@@ -17,6 +17,7 @@
 package com.android.server.permission.access.appop
 
 import android.app.AppOpsManager
+import android.util.Slog
 import com.android.server.permission.access.GetStateScope
 import com.android.server.permission.access.MutableAccessState
 import com.android.server.permission.access.MutateStateScope
@@ -87,6 +88,10 @@
         appOpName: String,
         mode: Int
     ): Boolean {
+        if (userId !in newState.userStates) {
+            Slog.e(LOG_TAG, "Unable to set app op mode for missing user $userId")
+            return false
+        }
         val defaultMode = AppOpsManager.opToDefaultMode(appOpName)
         val oldMode =
             newState.userStates[userId]!!
@@ -155,4 +160,8 @@
          */
         abstract fun onStateMutated()
     }
+
+    companion object {
+        private val LOG_TAG = PackageAppOpPolicy::class.java.simpleName
+    }
 }
diff --git a/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt
new file mode 100644
index 0000000..827dd0e
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/collection/LongSparseArrayExtensions.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.permission.access.collection
+
+import android.util.LongSparseArray
+
+inline fun <T> LongSparseArray<T>.allIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (!predicate(index, key, value)) {
+            return false
+        }
+    }
+    return true
+}
+
+inline fun <T> LongSparseArray<T>.anyIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            return true
+        }
+    }
+    return false
+}
+
+inline fun <T> LongSparseArray<T>.forEachIndexed(action: (Int, Long, T) -> Unit) {
+    for (index in 0 until size) {
+        action(index, keyAt(index), valueAt(index))
+    }
+}
+
+inline fun <T> LongSparseArray<T>.forEachReversedIndexed(action: (Int, Long, T) -> Unit) {
+    for (index in lastIndex downTo 0) {
+        action(index, keyAt(index), valueAt(index))
+    }
+}
+
+inline fun <T> LongSparseArray<T>.getOrPut(key: Long, defaultValue: () -> T): T {
+    val index = indexOfKey(key)
+    return if (index >= 0) {
+        valueAt(index)
+    } else {
+        defaultValue().also { put(key, it) }
+    }
+}
+
+inline val <T> LongSparseArray<T>.lastIndex: Int
+    get() = size - 1
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun <T> LongSparseArray<T>.minusAssign(key: Long) {
+    delete(key)
+}
+
+inline fun <T> LongSparseArray<T>.noneIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            return false
+        }
+    }
+    return true
+}
+
+inline fun <T> LongSparseArray<T>.removeAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+    var isChanged = false
+    forEachReversedIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            removeAt(index)
+            isChanged = true
+        }
+    }
+    return isChanged
+}
+
+inline fun <T> LongSparseArray<T>.retainAllIndexed(predicate: (Int, Long, T) -> Boolean): Boolean {
+    var isChanged = false
+    forEachReversedIndexed { index, key, value ->
+        if (!predicate(index, key, value)) {
+            removeAt(index)
+            isChanged = true
+        }
+    }
+    return isChanged
+}
+
+inline val <T> LongSparseArray<T>.size: Int
+    get() = size()
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun <T> LongSparseArray<T>.set(key: Long, value: T) {
+    put(key, value)
+}
diff --git a/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt
new file mode 100644
index 0000000..a582431
--- /dev/null
+++ b/services/permission/java/com/android/server/permission/access/collection/SparseIntArrayExtensions.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.server.permission.access.collection
+
+import android.util.SparseIntArray
+
+inline fun SparseIntArray.allIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (!predicate(index, key, value)) {
+            return false
+        }
+    }
+    return true
+}
+
+inline fun SparseIntArray.anyIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            return true
+        }
+    }
+    return false
+}
+
+inline fun SparseIntArray.forEachIndexed(action: (Int, Int, Int) -> Unit) {
+    for (index in 0 until size) {
+        action(index, keyAt(index), valueAt(index))
+    }
+}
+
+inline fun SparseIntArray.forEachReversedIndexed(action: (Int, Int, Int) -> Unit) {
+    for (index in lastIndex downTo 0) {
+        action(index, keyAt(index), valueAt(index))
+    }
+}
+
+inline fun SparseIntArray.getOrPut(key: Int, defaultValue: () -> Int): Int {
+    val index = indexOfKey(key)
+    return if (index >= 0) {
+        valueAt(index)
+    } else {
+        defaultValue().also { put(key, it) }
+    }
+}
+
+inline val SparseIntArray.lastIndex: Int
+    get() = size - 1
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun SparseIntArray.minusAssign(key: Int) {
+    delete(key)
+}
+
+inline fun SparseIntArray.noneIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+    forEachIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            return false
+        }
+    }
+    return true
+}
+
+fun SparseIntArray.remove(key: Int) {
+    delete(key)
+}
+
+fun SparseIntArray.remove(key: Int, defaultValue: Int): Int {
+    val index = indexOfKey(key)
+    return if (index >= 0) {
+        val oldValue = valueAt(index)
+        removeAt(index)
+        oldValue
+    } else {
+        defaultValue
+    }
+}
+
+inline fun SparseIntArray.removeAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+    var isChanged = false
+    forEachReversedIndexed { index, key, value ->
+        if (predicate(index, key, value)) {
+            removeAt(index)
+            isChanged = true
+        }
+    }
+    return isChanged
+}
+
+inline fun SparseIntArray.retainAllIndexed(predicate: (Int, Int, Int) -> Boolean): Boolean {
+    var isChanged = false
+    forEachReversedIndexed { index, key, value ->
+        if (!predicate(index, key, value)) {
+            removeAt(index)
+            isChanged = true
+        }
+    }
+    return isChanged
+}
+
+@Suppress("NOTHING_TO_INLINE")
+inline operator fun SparseIntArray.set(key: Int, value: Int) {
+    put(key, value)
+}
+
+inline val SparseIntArray.size: Int
+    get() = size()
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 022268d..62d2d7e 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -134,7 +134,9 @@
     ) {
         val changedPermissionNames = MutableIndexedSet<String>()
         packageNames.forEachIndexed { _, packageName ->
-            val packageState = newState.externalState.packageStates[packageName]!!
+            // The package may still be removed even if it was once notified as installed.
+            val packageState = newState.externalState.packageStates[packageName]
+                ?: return@forEachIndexed
             adoptPermissions(packageState, changedPermissionNames)
             addPermissionGroups(packageState)
             addPermissions(packageState, changedPermissionNames)
@@ -147,12 +149,14 @@
         }
 
         packageNames.forEachIndexed { _, packageName ->
-            val packageState = newState.externalState.packageStates[packageName]!!
+            val packageState = newState.externalState.packageStates[packageName]
+                ?: return@forEachIndexed
             val installedPackageState = if (isSystemUpdated) packageState else null
             evaluateAllPermissionStatesForPackage(packageState, installedPackageState)
         }
         packageNames.forEachIndexed { _, packageName ->
-            val packageState = newState.externalState.packageStates[packageName]!!
+            val packageState = newState.externalState.packageStates[packageName]
+                ?: return@forEachIndexed
             newState.externalState.userIds.forEachIndexed { _, userId ->
                 inheritImplicitPermissionStates(packageState.appId, userId)
             }
@@ -1607,6 +1611,13 @@
         flagMask: Int,
         flagValues: Int
     ): Boolean {
+        if (userId !in newState.userStates) {
+            // Despite that we check UserManagerInternal.exists() in PermissionService, we may still
+            // sometimes get race conditions between that check and the actual mutateState() call.
+            // This should rarely happen but at least we should not crash.
+            Slog.e(LOG_TAG, "Unable to update permission flags for missing user $userId")
+            return false
+        }
         val oldFlags =
             newState.userStates[userId]!!
                 .appIdPermissionFlags[appId]
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index f469ab5..b162a1b 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -2870,5 +2870,8 @@
             } else {
                 emptySet<String>()
             }
+
+        fun getFullerPermission(permissionName: String): String? =
+            FULLER_PERMISSIONS[permissionName]
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index ff91d34..92016df 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -20,11 +20,10 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.Mode.INVALID_MODE_ID;
 
-
 import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE;
 import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE;
-import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
 import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE;
+import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
 import static com.android.server.display.mode.VotesStorage.GLOBAL_ID;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -43,6 +42,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.DeviceConfigInterface;
+import android.test.mock.MockContentResolver;
 import android.view.Display;
 import android.view.DisplayInfo;
 
@@ -51,21 +51,26 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.sensors.SensorManagerInternal;
 
+import junitparams.JUnitParamsRunner;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import junitparams.JUnitParamsRunner;
-
-
 @SmallTest
 @RunWith(JUnitParamsRunner.class)
 public class DisplayObserverTest {
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
     private static final int EXTERNAL_DISPLAY = 1;
     private static final int MAX_WIDTH = 1920;
     private static final int MAX_HEIGHT = 1080;
@@ -120,6 +125,8 @@
         mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mResources = mock(Resources.class);
         when(mContext.getResources()).thenReturn(mResources);
+        MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+        when(mContext.getContentResolver()).thenReturn(resolver);
         when(mResources.getInteger(R.integer.config_externalDisplayPeakRefreshRate))
                 .thenReturn(0);
         when(mResources.getInteger(R.integer.config_externalDisplayPeakWidth))
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 116d5db..97767a5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -426,6 +426,8 @@
 
     @Test
     public void testGetNextConstraintDropTimeElapsedLocked() {
+        setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 200 * HOUR_IN_MILLIS);
+
         long nextTimeToDropNumConstraints;
 
         // no delay, deadline
@@ -457,15 +459,18 @@
 
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(130400100, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + 800000L + (200 * HOUR_IN_MILLIS) / 2,
+                nextTimeToDropNumConstraints);
         js.setNumDroppedFlexibleConstraints(1);
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(156320100L, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + 800000L + (200 * HOUR_IN_MILLIS) * 6 / 10,
+                nextTimeToDropNumConstraints);
         js.setNumDroppedFlexibleConstraints(2);
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(182240100L, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + 800000L + (200 * HOUR_IN_MILLIS) * 7 / 10,
+                nextTimeToDropNumConstraints);
 
         // no delay, no deadline
         jb = createJob(0);
@@ -473,15 +478,15 @@
 
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(129600100, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + (200 * HOUR_IN_MILLIS) / 2, nextTimeToDropNumConstraints);
         js.setNumDroppedFlexibleConstraints(1);
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(155520100L, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + (200 * HOUR_IN_MILLIS) * 6 / 10, nextTimeToDropNumConstraints);
         js.setNumDroppedFlexibleConstraints(2);
         nextTimeToDropNumConstraints = mFlexibilityController
                 .getNextConstraintDropTimeElapsedLocked(js);
-        assertEquals(181440100L, nextTimeToDropNumConstraints);
+        assertEquals(FROZEN_TIME + (200 * HOUR_IN_MILLIS) * 7 / 10, nextTimeToDropNumConstraints);
 
         // delay, deadline
         jb = createJob(0)
@@ -1328,7 +1333,7 @@
 
         final ArraySet<String> pkgs = new ArraySet<>();
         pkgs.add(js.getSourcePackageName());
-        when(mJobSchedulerService.getPackagesForUidLocked(mSourceUid)).thenReturn(pkgs);
+        doReturn(pkgs).when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid);
 
         setUidBias(mSourceUid, BIAS_TOP_APP);
         setUidBias(mSourceUid, BIAS_FOREGROUND_SERVICE);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 0045026..0831086 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -29,8 +29,6 @@
         "src/**/*.java",
         "src/**/*.kt",
 
-        "test-apps/JobTestApp/src/**/*.java",
-
         "test-apps/SuspendTestApp/src/**/*.java",
     ],
     static_libs: [
@@ -124,7 +122,6 @@
     },
 
     data: [
-        ":JobTestApp",
         ":SimpleServiceTestApp1",
         ":SimpleServiceTestApp2",
         ":SimpleServiceTestApp3",
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index b1d5039..27c522d 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -29,7 +29,6 @@
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
         <option name="test-file-name" value="FrameworksServicesTests.apk" />
-        <option name="test-file-name" value="JobTestApp.apk" />
         <option name="test-file-name" value="SuspendTestApp.apk" />
         <option name="test-file-name" value="SimpleServiceTestApp1.apk" />
         <option name="test-file-name" value="SimpleServiceTestApp2.apk" />
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
deleted file mode 100644
index e871fc5..0000000
--- a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.job;
-
-import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STARTED;
-import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
-import static com.android.servicestests.apps.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.job.JobParameters;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.IDeviceIdleController;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.servicestests.apps.jobtestapp.TestJobActivity;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests that background restrictions on jobs work as expected.
- * This test requires test-apps/JobTestApp to be installed on the device.
- * To run this test from root of checkout:
- * <pre>
- *  mmm -j32 frameworks/base/services/tests/servicestests/
- *  adb install -r $OUT/data/app/JobTestApp/JobTestApp.apk
- *  adb install -r $OUT/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
- *  adb  shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
- *  com.android.frameworks.servicestests
- * </pre>
- */
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class BackgroundRestrictionsTest {
-    private static final String TAG = BackgroundRestrictionsTest.class.getSimpleName();
-    private static final String TEST_APP_PACKAGE = "com.android.servicestests.apps.jobtestapp";
-    private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestJobActivity";
-    private static final long POLL_INTERVAL = 500;
-    private static final long DEFAULT_WAIT_TIMEOUT = 10_000;
-
-    private Context mContext;
-    private AppOpsManager mAppOpsManager;
-    private IDeviceIdleController mDeviceIdleController;
-    private IActivityManager mIActivityManager;
-    private volatile int mTestJobId = -1;
-    private int mTestPackageUid;
-    /* accesses must be synchronized on itself */
-    private final TestJobStatus mTestJobStatus = new TestJobStatus();
-    private final BroadcastReceiver mJobStateChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
-            Log.d(TAG, "Received action " + intent.getAction());
-            synchronized (mTestJobStatus) {
-                switch (intent.getAction()) {
-                    case ACTION_JOB_STARTED:
-                        mTestJobStatus.running = true;
-                        mTestJobStatus.jobId = params.getJobId();
-                        mTestJobStatus.stopReason = JobParameters.STOP_REASON_UNDEFINED;
-                        break;
-                    case ACTION_JOB_STOPPED:
-                        mTestJobStatus.running = false;
-                        mTestJobStatus.jobId = params.getJobId();
-                        mTestJobStatus.stopReason = params.getStopReason();
-                        break;
-                }
-            }
-        }
-    };
-
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
-                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-        mIActivityManager = ActivityManager.getService();
-        mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
-        mTestJobStatus.reset();
-        final IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_JOB_STARTED);
-        intentFilter.addAction(ACTION_JOB_STOPPED);
-        mContext.registerReceiver(mJobStateChangeReceiver, intentFilter,
-                Context.RECEIVER_EXPORTED_UNAUDITED);
-        setAppOpsModeAllowed(true);
-        setPowerExemption(false);
-    }
-
-    private void scheduleTestJob() {
-        mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
-        final Intent scheduleJobIntent = new Intent(TestJobActivity.ACTION_START_JOB);
-        scheduleJobIntent.putExtra(TestJobActivity.EXTRA_JOB_ID_KEY, mTestJobId);
-        scheduleJobIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
-        mContext.startActivity(scheduleJobIntent);
-    }
-
-    private void scheduleAndAssertJobStarted() throws Exception {
-        scheduleTestJob();
-        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
-        assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
-    }
-
-    @FlakyTest
-    @Test
-    public void testPowerExemption() throws Exception {
-        scheduleAndAssertJobStarted();
-        setAppOpsModeAllowed(false);
-        mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
-        assertTrue("Job did not stop after putting app under bg-restriction",
-                awaitJobStop(DEFAULT_WAIT_TIMEOUT,
-                        JobParameters.STOP_REASON_BACKGROUND_RESTRICTION));
-
-        setPowerExemption(true);
-        scheduleTestJob();
-        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
-        assertTrue("Job did not start when the app was in the power exemption list",
-                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
-
-        setPowerExemption(false);
-        assertTrue("Job did not stop after removing from the power exemption list",
-                awaitJobStop(DEFAULT_WAIT_TIMEOUT,
-                        JobParameters.STOP_REASON_BACKGROUND_RESTRICTION));
-
-        scheduleTestJob();
-        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
-        assertFalse("Job started under bg-restrictions", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
-        setPowerExemption(true);
-        assertTrue("Job did not start when the app was in the power exemption list",
-                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        final Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
-        cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
-        cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(cancelJobsIntent);
-        mContext.unregisterReceiver(mJobStateChangeReceiver);
-        Thread.sleep(500); // To avoid race with register in the next setUp
-        setAppOpsModeAllowed(true);
-        setPowerExemption(false);
-    }
-
-    private void setPowerExemption(boolean exempt) throws RemoteException {
-        if (exempt) {
-            mDeviceIdleController.addPowerSaveWhitelistApp(TEST_APP_PACKAGE);
-        } else {
-            mDeviceIdleController.removePowerSaveWhitelistApp(TEST_APP_PACKAGE);
-        }
-    }
-
-    private void setAppOpsModeAllowed(boolean allow) {
-        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mTestPackageUid,
-                TEST_APP_PACKAGE, allow ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
-    }
-
-    private boolean awaitJobStart(long timeout) throws InterruptedException {
-        return waitUntilTrue(timeout, () -> {
-            synchronized (mTestJobStatus) {
-                return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
-            }
-        });
-    }
-
-    private boolean awaitJobStop(long timeout, @JobParameters.StopReason int expectedStopReason)
-            throws InterruptedException {
-        return waitUntilTrue(timeout, () -> {
-            synchronized (mTestJobStatus) {
-                return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running
-                        && (expectedStopReason == JobParameters.STOP_REASON_UNDEFINED
-                        || mTestJobStatus.stopReason == expectedStopReason);
-            }
-        });
-    }
-
-    private boolean waitUntilTrue(long timeout, Condition condition) throws InterruptedException {
-        final long deadLine = SystemClock.uptimeMillis() + timeout;
-        do {
-            Thread.sleep(POLL_INTERVAL);
-        } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
-        return condition.isTrue();
-    }
-
-    private static final class TestJobStatus {
-        int jobId;
-        int stopReason;
-        boolean running;
-
-        private void reset() {
-            running = false;
-            stopReason = jobId = 0;
-        }
-    }
-
-    private interface Condition {
-        boolean isTrue();
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 0f5fb91..d50affb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -406,8 +406,8 @@
 
     public void testPushDynamicShortcut() {
         // Change the max number of shortcuts.
-        mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5");
-
+        mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
+                + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
         setCaller(CALLING_PACKAGE_1, USER_0);
 
         final ShortcutInfo s1 = makeShortcut("s1");
@@ -545,6 +545,57 @@
                 eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
     }
 
+    public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
+            throws InterruptedException {
+        mService.updateConfigurationLocked(
+                ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");
+
+        // Verify calls to UsageStatsManagerInternal#reportShortcutUsage are throttled.
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        {
+            final ShortcutInfo si = makeShortcut("s0");
+            mManager.pushDynamicShortcut(si);
+        }
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), eq("s0"), eq(USER_0));
+        Mockito.reset(mMockUsageStatsManagerInternal);
+        for (int i = 2; i <= 10; i++) {
+            final ShortcutInfo si = makeShortcut("s" + i);
+            mManager.pushDynamicShortcut(si);
+        }
+        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+                any(), any(), anyInt());
+
+        // Verify pkg2 isn't blocked by pkg1, but consecutive calls from pkg2 are throttled as well.
+        setCaller(CALLING_PACKAGE_2, USER_0);
+        {
+            final ShortcutInfo si = makeShortcut("s1");
+            mManager.pushDynamicShortcut(si);
+        }
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_2), eq("s1"), eq(USER_0));
+        Mockito.reset(mMockUsageStatsManagerInternal);
+        for (int i = 2; i <= 10; i++) {
+            final ShortcutInfo si = makeShortcut("s" + i);
+            mManager.pushDynamicShortcut(si);
+        }
+        verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+                any(), any(), anyInt());
+
+        Mockito.reset(mMockUsageStatsManagerInternal);
+        // Let time passes which resets the throttle
+        Thread.sleep(505);
+        // Verify UsageStatsManagerInternal#reportShortcutUsed can be called again
+        setCaller(CALLING_PACKAGE_1, USER_0);
+        mManager.pushDynamicShortcut(makeShortcut("s10"));
+        setCaller(CALLING_PACKAGE_2, USER_0);
+        mManager.pushDynamicShortcut(makeShortcut("s10"));
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_1), any(), eq(USER_0));
+        verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+                eq(CALLING_PACKAGE_2), any(), eq(USER_0));
+    }
+
     public void testUnlimitedCalls() {
         setCaller(CALLING_PACKAGE_1, USER_0);
 
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.bp b/services/tests/servicestests/test-apps/JobTestApp/Android.bp
deleted file mode 100644
index 6458bcd..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // 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"],
-}
-
-android_test_helper_app {
-    name: "JobTestApp",
-
-    sdk_version: "current",
-
-    srcs: ["**/*.java"],
-
-    dex_preopt: {
-        enabled: false,
-    },
-    optimize: {
-        enabled: false,
-    },
-}
diff --git a/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml
deleted file mode 100644
index ac35805..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.servicestests.apps.jobtestapp">
-
-    <application>
-        <service android:name=".TestJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
-        <activity android:name=".TestJobActivity"
-                  android:exported="true" />
-    </application>
-
-</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/OWNERS b/services/tests/servicestests/test-apps/JobTestApp/OWNERS
deleted file mode 100644
index 6f207fb1..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /apex/jobscheduler/OWNERS
diff --git a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java
deleted file mode 100644
index 99eb196..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.servicestests.apps.jobtestapp;
-
-import android.app.Activity;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestJobActivity extends Activity {
-    private static final String TAG = TestJobActivity.class.getSimpleName();
-    private static final String PACKAGE_NAME = "com.android.servicestests.apps.jobtestapp";
-
-    public static final String EXTRA_JOB_ID_KEY = PACKAGE_NAME + ".extra.JOB_ID";
-    public static final String ACTION_START_JOB = PACKAGE_NAME + ".action.START_JOB";
-    public static final String ACTION_CANCEL_JOBS = PACKAGE_NAME + ".action.CANCEL_JOBS";
-    public static final int JOB_INITIAL_BACKOFF = 10_000;
-    public static final int JOB_MINIMUM_LATENCY = 5_000;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        ComponentName jobServiceComponent = new ComponentName(this, TestJobService.class);
-        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        final Intent intent = getIntent();
-        switch (intent.getAction()) {
-            case ACTION_CANCEL_JOBS:
-                jobScheduler.cancelAll();
-                Log.d(TAG, "Cancelled all jobs for " + getPackageName());
-                break;
-            case ACTION_START_JOB:
-                final int jobId = intent.getIntExtra(EXTRA_JOB_ID_KEY, hashCode());
-                JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobServiceComponent)
-                        .setBackoffCriteria(JOB_INITIAL_BACKOFF, JobInfo.BACKOFF_POLICY_LINEAR)
-                        .setMinimumLatency(JOB_MINIMUM_LATENCY)
-                        .setOverrideDeadline(JOB_MINIMUM_LATENCY);
-                final int result = jobScheduler.schedule(jobBuilder.build());
-                if (result != JobScheduler.RESULT_SUCCESS) {
-                    Log.e(TAG, "Could not schedule job " + jobId);
-                } else {
-                    Log.d(TAG, "Successfully scheduled job with id " + jobId);
-                }
-                break;
-            default:
-                Log.e(TAG, "Unknown action " + intent.getAction());
-        }
-        finish();
-    }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java
deleted file mode 100644
index b8585f2..0000000
--- a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.servicestests.apps.jobtestapp;
-
-import android.annotation.TargetApi;
-import android.app.job.JobParameters;
-import android.app.job.JobService;
-import android.content.Intent;
-import android.util.Log;
-
-@TargetApi(24)
-public class TestJobService extends JobService {
-    private static final String TAG = TestJobService.class.getSimpleName();
-    private static final String PACKAGE_NAME = "com.android.servicestests.apps.jobtestapp";
-    public static final String ACTION_JOB_STARTED = PACKAGE_NAME + ".action.JOB_STARTED";
-    public static final String ACTION_JOB_STOPPED = PACKAGE_NAME + ".action.JOB_STOPPED";
-    public static final String JOB_PARAMS_EXTRA_KEY = PACKAGE_NAME + ".extra.JOB_PARAMETERS";
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        Log.i(TAG, "Test job executing: " + params.getJobId());
-        Intent reportJobStartIntent = new Intent(ACTION_JOB_STARTED);
-        reportJobStartIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params)
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        sendBroadcast(reportJobStartIntent);
-        return true;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters params) {
-        Log.i(TAG, "Test job stopped executing: " + params.getJobId());
-        Intent reportJobStopIntent = new Intent(ACTION_JOB_STOPPED);
-        reportJobStopIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params)
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        sendBroadcast(reportJobStopIntent);
-        // Deadline constraint is dropped on reschedule, so it's more reliable to use a new job.
-        return false;
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1f35cc..723ac15 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -13792,8 +13792,7 @@
         NotificationManager.Policy policy = new NotificationManager.Policy(0, 0, 0);
         mBinderService.setNotificationPolicy("package", policy, false);
 
-        verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy),
-                eq(ZenModeConfig.UPDATE_ORIGIN_APP));
+        verify(zenHelper).applyGlobalPolicyAsImplicitZenRule(eq("package"), anyInt(), eq(policy));
     }
 
     @Test
@@ -13859,7 +13858,7 @@
             verify(zenModeHelper).setNotificationPolicy(eq(policy), anyInt(), anyInt());
         } else {
             verify(zenModeHelper).applyGlobalPolicyAsImplicitZenRule(anyString(), anyInt(),
-                    eq(policy), anyInt());
+                    eq(policy));
         }
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
index 3d8ec2e..f604f1e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenDeviceEffectsTest.java
@@ -52,7 +52,6 @@
                 .setShouldMaximizeDoze(true)
                 .setShouldUseNightMode(false)
                 .setShouldSuppressAmbientDisplay(false).setShouldSuppressAmbientDisplay(true)
-                .setUserModifiedFields(8)
                 .build();
 
         assertThat(deviceEffects.shouldDimWallpaper()).isTrue();
@@ -65,7 +64,6 @@
         assertThat(deviceEffects.shouldMinimizeRadioUsage()).isFalse();
         assertThat(deviceEffects.shouldUseNightMode()).isFalse();
         assertThat(deviceEffects.shouldSuppressAmbientDisplay()).isTrue();
-        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(8);
     }
 
     @Test
@@ -97,7 +95,6 @@
                 .setShouldMinimizeRadioUsage(true)
                 .setShouldUseNightMode(true)
                 .setShouldSuppressAmbientDisplay(true)
-                .setUserModifiedFields(6)
                 .build();
 
         Parcel parcel = Parcel.obtain();
@@ -116,7 +113,6 @@
         assertThat(copy.shouldUseNightMode()).isTrue();
         assertThat(copy.shouldSuppressAmbientDisplay()).isTrue();
         assertThat(copy.shouldDisplayGrayscale()).isFalse();
-        assertThat(copy.getUserModifiedFields()).isEqualTo(6);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index db92719..e523e79f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -285,7 +285,6 @@
         assertEquals(expected.getPriorityCallSenders(), actual.getPriorityCallSenders());
         assertEquals(expected.getPriorityMessageSenders(), actual.getPriorityMessageSenders());
         assertEquals(expected.getPriorityChannels(), actual.getPriorityChannels());
-        assertEquals(expected.getUserModifiedFields(), actual.getUserModifiedFields());
     }
 
     @Test
@@ -342,45 +341,32 @@
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.zenPolicy = null;
         rule.zenDeviceEffects = null;
-
         assertThat(rule.canBeUpdatedByApp()).isTrue();
 
         rule.userModifiedFields = 1;
+
         assertThat(rule.canBeUpdatedByApp()).isFalse();
     }
 
     @Test
     public void testCanBeUpdatedByApp_policyModified() throws Exception {
-        ZenPolicy.Builder policyBuilder = new ZenPolicy.Builder().setUserModifiedFields(0);
-        ZenPolicy policy = policyBuilder.build();
-
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-        rule.zenPolicy = policy;
-
-        assertThat(rule.userModifiedFields).isEqualTo(0);
+        rule.zenPolicy = new ZenPolicy();
         assertThat(rule.canBeUpdatedByApp()).isTrue();
 
-        policy = policyBuilder.setUserModifiedFields(1).build();
-        assertThat(policy.getUserModifiedFields()).isEqualTo(1);
-        rule.zenPolicy = policy;
+        rule.zenPolicyUserModifiedFields = 1;
+
         assertThat(rule.canBeUpdatedByApp()).isFalse();
     }
 
     @Test
     public void testCanBeUpdatedByApp_deviceEffectsModified() throws Exception {
-        ZenDeviceEffects.Builder deviceEffectsBuilder =
-                new ZenDeviceEffects.Builder().setUserModifiedFields(0);
-        ZenDeviceEffects deviceEffects = deviceEffectsBuilder.build();
-
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
-        rule.zenDeviceEffects = deviceEffects;
-
-        assertThat(rule.userModifiedFields).isEqualTo(0);
+        rule.zenDeviceEffects = new ZenDeviceEffects.Builder().build();
         assertThat(rule.canBeUpdatedByApp()).isTrue();
 
-        deviceEffects = deviceEffectsBuilder.setUserModifiedFields(1).build();
-        assertThat(deviceEffects.getUserModifiedFields()).isEqualTo(1);
-        rule.zenDeviceEffects = deviceEffects;
+        rule.zenDeviceEffectsUserModifiedFields = 1;
+
         assertThat(rule.canBeUpdatedByApp()).isFalse();
     }
 
@@ -406,6 +392,8 @@
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
         rule.userModifiedFields = 16;
+        rule.zenPolicyUserModifiedFields = 5;
+        rule.zenDeviceEffectsUserModifiedFields = 2;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
         rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
@@ -432,6 +420,9 @@
         assertEquals(rule.iconResName, parceled.iconResName);
         assertEquals(rule.type, parceled.type);
         assertEquals(rule.userModifiedFields, parceled.userModifiedFields);
+        assertEquals(rule.zenPolicyUserModifiedFields, parceled.zenPolicyUserModifiedFields);
+        assertEquals(rule.zenDeviceEffectsUserModifiedFields,
+                parceled.zenDeviceEffectsUserModifiedFields);
         assertEquals(rule.triggerDescription, parceled.triggerDescription);
         assertEquals(rule.zenPolicy, parceled.zenPolicy);
         assertEquals(rule.deletionInstant, parceled.deletionInstant);
@@ -511,6 +502,8 @@
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
         rule.userModifiedFields = 4;
+        rule.zenPolicyUserModifiedFields = 5;
+        rule.zenDeviceEffectsUserModifiedFields = 2;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
         rule.deletionInstant = Instant.ofEpochMilli(1701790147000L);
@@ -541,6 +534,9 @@
         assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation);
         assertEquals(rule.type, fromXml.type);
         assertEquals(rule.userModifiedFields, fromXml.userModifiedFields);
+        assertEquals(rule.zenPolicyUserModifiedFields, fromXml.zenPolicyUserModifiedFields);
+        assertEquals(rule.zenDeviceEffectsUserModifiedFields,
+                fromXml.zenDeviceEffectsUserModifiedFields);
         assertEquals(rule.triggerDescription, fromXml.triggerDescription);
         assertEquals(rule.iconResName, fromXml.iconResName);
         assertEquals(rule.deletionInstant, fromXml.deletionInstant);
@@ -697,7 +693,6 @@
                 .allowPriorityChannels(false)
                 .hideAllVisualEffects()
                 .showVisualEffect(ZenPolicy.VISUAL_EFFECT_AMBIENT, true)
-                .setUserModifiedFields(4)
                 .build();
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -732,7 +727,6 @@
         assertEquals(policy.getVisualEffectAmbient(), fromXml.getVisualEffectAmbient());
         assertEquals(policy.getVisualEffectNotificationList(),
                 fromXml.getVisualEffectNotificationList());
-        assertEquals(policy.getUserModifiedFields(), fromXml.getUserModifiedFields());
     }
 
     private ZenModeConfig getMutedRingerConfig() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 9d7cf53..2e64645 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -73,13 +73,15 @@
                     : Set.of("version", "manualRule", "automaticRules");
 
     // Differences for flagged fields are only generated if the flag is enabled.
-    // "Metadata" fields (userModifiedFields, deletionInstant) are not compared.
+    // "Metadata" fields (userModifiedFields & co, deletionInstant) are not compared.
     private static final Set<String> ZEN_RULE_EXEMPT_FIELDS =
             android.app.Flags.modesApi()
-                    ? Set.of("userModifiedFields", "deletionInstant")
+                    ? Set.of("userModifiedFields", "zenPolicyUserModifiedFields",
+                            "zenDeviceEffectsUserModifiedFields", "deletionInstant")
                     : Set.of(RuleDiff.FIELD_TYPE, RuleDiff.FIELD_TRIGGER_DESCRIPTION,
                             RuleDiff.FIELD_ICON_RES, RuleDiff.FIELD_ALLOW_MANUAL,
                             RuleDiff.FIELD_ZEN_DEVICE_EFFECTS, "userModifiedFields",
+                            "zenPolicyUserModifiedFields", "zenDeviceEffectsUserModifiedFields",
                             "deletionInstant");
 
     // allowPriorityChannels is flagged by android.app.modes_api
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 78fe41f..edc876a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -46,6 +46,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.SOURCE_SCHEDULE;
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
@@ -67,6 +68,7 @@
 import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
+import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
@@ -295,6 +297,8 @@
         when(appInfoSpy.loadLabel(any())).thenReturn(CUSTOM_APP_LABEL);
         when(mPackageManager.getApplicationInfo(eq(CUSTOM_PKG_NAME), anyInt()))
                 .thenReturn(appInfoSpy);
+        when(mPackageManager.getApplicationInfo(eq(mContext.getPackageName()), anyInt()))
+                .thenReturn(appInfoSpy);
         mZenModeHelper.mPm = mPackageManager;
 
         mZenModeEventLogger.reset();
@@ -2236,12 +2240,7 @@
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
-        // savedRule.getDeviceEffects() is equal to zde, except for the userModifiedFields.
-        // So we clear before comparing.
-        ZenDeviceEffects savedEffects = new ZenDeviceEffects.Builder(savedRule.getDeviceEffects())
-                .setUserModifiedFields(0).build();
-
-        assertThat(savedEffects).isEqualTo(zde);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
     }
 
     @Test
@@ -2331,12 +2330,7 @@
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
-        // savedRule.getDeviceEffects() is equal to updateFromUser, except for the
-        // userModifiedFields, so we clear before comparing.
-        ZenDeviceEffects savedEffects = new ZenDeviceEffects.Builder(savedRule.getDeviceEffects())
-                .setUserModifiedFields(0).build();
-
-        assertThat(savedEffects).isEqualTo(updateFromUser);
+        assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
     }
 
     @Test
@@ -3411,7 +3405,6 @@
 
         rule.allowManualInvocation = ALLOW_MANUAL;
         rule.type = TYPE;
-        rule.userModifiedFields = AutomaticZenRule.FIELD_NAME;
         rule.iconResName = ICON_RES_NAME;
         rule.triggerDescription = TRIGGER_DESC;
 
@@ -3426,7 +3419,6 @@
         assertEquals(POLICY, actual.getZenPolicy());
         assertEquals(CONFIG_ACTIVITY, actual.getConfigurationActivity());
         assertEquals(TYPE, actual.getType());
-        assertEquals(AutomaticZenRule.FIELD_NAME, actual.getUserModifiedFields());
         assertEquals(ALLOW_MANUAL, actual.isManualInvocationAllowed());
         assertEquals(CREATION_TIME, actual.getCreationTime());
         assertEquals(OWNER.getPackageName(), actual.getPackageName());
@@ -3453,29 +3445,31 @@
                 .setManualInvocationAllowed(ALLOW_MANUAL)
                 .build();
 
-        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(OWNER.getPackageName(), azr,
+                UPDATE_ORIGIN_APP, "add", CUSTOM_PKG_UID);
 
-        mZenModeHelper.populateZenRule(OWNER.getPackageName(), azr, rule, UPDATE_ORIGIN_APP, true);
+        ZenModeConfig.ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
 
-        assertEquals(NAME, rule.name);
-        assertEquals(OWNER, rule.component);
-        assertEquals(CONDITION_ID, rule.conditionId);
-        assertEquals(INTERRUPTION_FILTER_ZR, rule.zenMode);
-        assertEquals(ENABLED, rule.enabled);
-        assertEquals(POLICY, rule.zenPolicy);
-        assertEquals(CONFIG_ACTIVITY, rule.configurationActivity);
-        assertEquals(TYPE, rule.type);
-        assertEquals(ALLOW_MANUAL, rule.allowManualInvocation);
-        assertEquals(OWNER.getPackageName(), rule.getPkg());
-        assertEquals(ICON_RES_NAME, rule.iconResName);
+        assertThat(storedRule).isNotNull();
+        assertEquals(NAME, storedRule.name);
+        assertEquals(OWNER, storedRule.component);
+        assertEquals(CONDITION_ID, storedRule.conditionId);
+        assertEquals(INTERRUPTION_FILTER_ZR, storedRule.zenMode);
+        assertEquals(ENABLED, storedRule.enabled);
+        assertEquals(POLICY, storedRule.zenPolicy);
+        assertEquals(CONFIG_ACTIVITY, storedRule.configurationActivity);
+        assertEquals(TYPE, storedRule.type);
+        assertEquals(ALLOW_MANUAL, storedRule.allowManualInvocation);
+        assertEquals(OWNER.getPackageName(), storedRule.getPkg());
+        assertEquals(ICON_RES_NAME, storedRule.iconResName);
         // Because the origin of the update is the app, we don't expect the bitmask to change.
-        assertEquals(0, rule.userModifiedFields);
-        assertEquals(TRIGGER_DESC, rule.triggerDescription);
+        assertEquals(0, storedRule.userModifiedFields);
+        assertEquals(TRIGGER_DESC, storedRule.triggerDescription);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_updatesNameUnlessUserModified() {
+    public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() {
         // Add a starting rule with the name OriginalName.
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
@@ -3492,7 +3486,6 @@
                 Process.SYSTEM_UID);
         rule = mZenModeHelper.getAutomaticZenRule(ruleId);
         assertThat(rule.getName()).isEqualTo("NewName");
-        assertThat(rule.canUpdate()).isTrue();
 
         // The user modifies some other field in the rule, which makes the rule as a whole not
         // app modifiable.
@@ -3501,10 +3494,6 @@
                 .build();
         mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_USER, "reason",
                 Process.SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.getUserModifiedFields())
-                .isEqualTo(AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
-        assertThat(rule.canUpdate()).isFalse();
 
         // ...but the app can still modify the name, because the name itself hasn't been modified
         // by the user.
@@ -3524,8 +3513,6 @@
                 Process.SYSTEM_UID);
         rule = mZenModeHelper.getAutomaticZenRule(ruleId);
         assertThat(rule.getName()).isEqualTo("UserProvidedName");
-        assertThat(rule.getUserModifiedFields()).isEqualTo(AutomaticZenRule.FIELD_NAME
-                | AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
 
         // The app is no longer able to modify the name.
         azrUpdate = new AutomaticZenRule.Builder(rule)
@@ -3539,7 +3526,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_updatesBitmaskAndValueForUserOrigin() {
+    public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setZenPolicy(new ZenPolicy.Builder().build())
@@ -3571,84 +3558,21 @@
 
         // UPDATE_ORIGIN_USER should change the bitmask and change the values.
         assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
-        assertThat(rule.getUserModifiedFields())
-                .isEqualTo(AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
-        assertThat(rule.getZenPolicy().getUserModifiedFields())
-                .isEqualTo(ZenPolicy.FIELD_ALLOW_CHANNELS);
         assertThat(rule.getZenPolicy().getPriorityChannels()).isEqualTo(ZenPolicy.STATE_ALLOW);
-        assertThat(rule.getDeviceEffects().getUserModifiedFields())
-                .isEqualTo(ZenDeviceEffects.FIELD_GRAYSCALE);
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.userModifiedFields)
+                .isEqualTo(AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
+        assertThat(storedRule.zenPolicyUserModifiedFields)
+                .isEqualTo(ZenPolicy.FIELD_ALLOW_CHANNELS);
+        assertThat(storedRule.zenDeviceEffectsUserModifiedFields)
+                .isEqualTo(ZenDeviceEffects.FIELD_GRAYSCALE);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_doesNotUpdateValuesForInitUserOrigin() {
-        // Adds a starting rule with empty zen policies and device effects
-        AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
-                .setInterruptionFilter(INTERRUPTION_FILTER_ALL) // Already the default, no change
-                .setZenPolicy(new ZenPolicy.Builder()
-                        .allowReminders(false)
-                        .build())
-                .setDeviceEffects(new ZenDeviceEffects.Builder()
-                        .setShouldDisplayGrayscale(false)
-                        .build())
-                .build();
-        // Adds the rule using the user, to set user-modified bits.
-        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, UPDATE_ORIGIN_USER, "reason", Process.SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.canUpdate()).isFalse();
-        assertThat(rule.getUserModifiedFields()).isEqualTo(AutomaticZenRule.FIELD_NAME);
-
-        ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
-                .allowReminders(true)
-                .build();
-        ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder(rule.getDeviceEffects())
-                .setShouldDisplayGrayscale(true)
-                .build();
-        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(policy)
-                .setDeviceEffects(deviceEffects)
-                .build();
-
-        // Attempts to update the rule with the AZR from origin init user.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_INIT_USER, "reason",
-                Process.SYSTEM_UID);
-        AutomaticZenRule unchangedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
-
-        // UPDATE_ORIGIN_INIT_USER does not change the bitmask or values if rule is user modified.
-        // TODO: b/318506692 - Remove once we check that INIT origins can't call add/updateAZR.
-        assertThat(unchangedRule.getUserModifiedFields()).isEqualTo(rule.getUserModifiedFields());
-        assertThat(unchangedRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
-        assertThat(unchangedRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
-                rule.getZenPolicy().getUserModifiedFields());
-        assertThat(unchangedRule.getZenPolicy().getPriorityCategoryReminders()).isEqualTo(
-                ZenPolicy.STATE_DISALLOW);
-        assertThat(unchangedRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
-                rule.getDeviceEffects().getUserModifiedFields());
-        assertThat(unchangedRule.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
-
-        // Creates a new rule with the AZR from origin init user.
-        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrUpdate, UPDATE_ORIGIN_INIT_USER, "reason", Process.SYSTEM_UID);
-        AutomaticZenRule newRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
-
-        // UPDATE_ORIGIN_INIT_USER does change the values if the rule is new,
-        // but does not update the bitmask.
-        assertThat(newRule.getUserModifiedFields()).isEqualTo(0);
-        assertThat(newRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
-        assertThat(newRule.getZenPolicy().getUserModifiedFields()).isEqualTo(0);
-        assertThat(newRule.getZenPolicy().getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
-        assertThat(newRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(0);
-        assertThat(newRule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_updatesValuesForSystemUiOrigin() {
+    public void updateAutomaticZenRule_fromSystemUi_updatesValues() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
@@ -3684,17 +3608,19 @@
         rule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
         // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
-        assertThat(rule.getUserModifiedFields()).isEqualTo(0);
-        assertThat(rule.getZenPolicy().getUserModifiedFields()).isEqualTo(0);
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
                 .isEqualTo(ZenPolicy.STATE_ALLOW);
-        assertThat(rule.getDeviceEffects().getUserModifiedFields()).isEqualTo(0);
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.userModifiedFields).isEqualTo(0);
+        assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0);
+        assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(0);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_updatesValuesIfRuleNotUserModified() {
+    public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
@@ -3709,7 +3635,6 @@
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.canUpdate()).isTrue();
 
         ZenPolicy policy = new ZenPolicy.Builder()
                 .allowReminders(true)
@@ -3717,57 +3642,59 @@
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
                 .build();
-        AutomaticZenRule azrUpdate =  new AutomaticZenRule.Builder(rule)
+        AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .setZenPolicy(policy)
                 .setDeviceEffects(deviceEffects)
                 .build();
 
-        // Since the rule is not already user modified, UPDATE_ORIGIN_UNKNOWN can modify the rule.
+        // Since the rule is not already user modified, UPDATE_ORIGIN_APP can modify the rule.
         // The bitmask is not modified.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_UNKNOWN, "reason",
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azrUpdate, UPDATE_ORIGIN_APP, "reason",
                 Process.SYSTEM_UID);
-        AutomaticZenRule unchangedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
-        assertThat(unchangedRule.getUserModifiedFields()).isEqualTo(rule.getUserModifiedFields());
-        assertThat(unchangedRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
-        assertThat(unchangedRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
-                rule.getZenPolicy().getUserModifiedFields());
-        assertThat(unchangedRule.getZenPolicy().getPriorityCategoryReminders())
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.userModifiedFields).isEqualTo(0);
+
+        assertThat(storedRule.zenMode).isEqualTo(ZEN_MODE_ALARMS);
+        assertThat(storedRule.zenPolicy.getPriorityCategoryReminders())
                 .isEqualTo(ZenPolicy.STATE_ALLOW);
-        assertThat(unchangedRule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
-                rule.getDeviceEffects().getUserModifiedFields());
-        assertThat(unchangedRule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+        assertThat(storedRule.zenDeviceEffects.shouldDisplayGrayscale()).isTrue();
+        assertThat(storedRule.userModifiedFields).isEqualTo(0);
+        assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0);
+        assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(0);
 
         // Creates another rule, this time from user. This will have user modified bits set.
         String ruleIdUser = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 azrBase, UPDATE_ORIGIN_USER, "reason", Process.SYSTEM_UID);
-        AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
-        assertThat(ruleUser.canUpdate()).isFalse();
+        storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser);
+        int ruleModifiedFields = storedRule.userModifiedFields;
+        int rulePolicyModifiedFields = storedRule.zenPolicyUserModifiedFields;
+        int ruleDeviceEffectsModifiedFields = storedRule.zenDeviceEffectsUserModifiedFields;
 
-        // Zen rule update coming from unknown origin. This cannot fully update the rule, because
+        // Zen rule update coming from the app again. This cannot fully update the rule, because
         // the rule is already considered user modified.
-        mZenModeHelper.updateAutomaticZenRule(ruleIdUser, azrUpdate, UPDATE_ORIGIN_UNKNOWN,
+        mZenModeHelper.updateAutomaticZenRule(ruleIdUser, azrUpdate, UPDATE_ORIGIN_APP,
                 "reason", Process.SYSTEM_UID);
-        ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
+        AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(ruleIdUser);
 
-        // UPDATE_ORIGIN_UNKNOWN can only change the value if the rule is not already user modified,
+        // The app can only change the value if the rule is not already user modified,
         // so the rule is not changed, and neither is the bitmask.
         assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
-        // Interruption Filter All is the default value, so it's not included as a modified field.
-        assertThat(ruleUser.getUserModifiedFields() | AutomaticZenRule.FIELD_NAME).isGreaterThan(0);
-        assertThat(ruleUser.getZenPolicy().getUserModifiedFields()
-                | ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS).isGreaterThan(0);
         assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders())
                 .isEqualTo(ZenPolicy.STATE_DISALLOW);
-        assertThat(ruleUser.getDeviceEffects().getUserModifiedFields()
-                | ZenDeviceEffects.FIELD_GRAYSCALE).isGreaterThan(0);
         assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
+
+        storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser);
+        assertThat(storedRule.userModifiedFields).isEqualTo(ruleModifiedFields);
+        assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(rulePolicyModifiedFields);
+        assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(
+                ruleDeviceEffectsModifiedFields);
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_updatesValuesIfRuleNew() {
+    public void addAutomaticZenRule_updatesValues() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
@@ -3778,21 +3705,22 @@
                         .setShouldDisplayGrayscale(true)
                         .build())
                 .build();
-        // Adds the rule using origin unknown, to show that a new rule is always allowed.
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                azrBase, UPDATE_ORIGIN_UNKNOWN, "reason", Process.SYSTEM_UID);
+                azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
         // The values are modified but the bitmask is not.
-        assertThat(rule.canUpdate()).isTrue();
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
                 .isEqualTo(ZenPolicy.STATE_ALLOW);
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.canBeUpdatedByApp()).isTrue();
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_nullDeviceEffectsUpdate() {
+    public void updateAutomaticZenRule_nullDeviceEffectsUpdate() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setDeviceEffects(new ZenDeviceEffects.Builder().build())
@@ -3807,9 +3735,9 @@
                 .setDeviceEffects(null)
                 .build();
 
-        // Zen rule update coming from unknown origin, but since the rule isn't already
+        // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_UNKNOWN, "reason",
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_APP, "reason",
                 Process.SYSTEM_UID);
         rule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
@@ -3819,7 +3747,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_API)
-    public void automaticZenRuleToZenRule_nullPolicyUpdate() {
+    public void updateAutomaticZenRule_nullPolicyUpdate() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
                 .setZenPolicy(new ZenPolicy.Builder().build())
@@ -3828,16 +3756,15 @@
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.canUpdate()).isTrue();
 
         AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
                 // Set zen policy to null
                 .setZenPolicy(null)
                 .build();
 
-        // Zen rule update coming from unknown origin, but since the rule isn't already
+        // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
-        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_UNKNOWN, "reason",
+        mZenModeHelper.updateAutomaticZenRule(ruleId, azr, UPDATE_ORIGIN_APP, "reason",
                 Process.SYSTEM_UID);
         rule = mZenModeHelper.getAutomaticZenRule(ruleId);
 
@@ -3859,7 +3786,6 @@
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.canUpdate()).isTrue();
 
         // Create a fully populated ZenPolicy.
         ZenPolicy policy = new ZenPolicy.Builder()
@@ -3894,8 +3820,10 @@
         // New ZenPolicy differs from the default config
         assertThat(rule.getZenPolicy()).isNotNull();
         assertThat(rule.getZenPolicy().getPriorityChannels()).isEqualTo(ZenPolicy.STATE_DISALLOW);
-        assertThat(rule.canUpdate()).isFalse();
-        assertThat(rule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+        assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(
                 ZenPolicy.FIELD_ALLOW_CHANNELS
                 | ZenPolicy.FIELD_PRIORITY_CATEGORY_REMINDERS
                 | ZenPolicy.FIELD_PRIORITY_CATEGORY_EVENTS
@@ -3918,7 +3846,6 @@
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 azrBase, UPDATE_ORIGIN_APP, "reason", Process.SYSTEM_UID);
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
-        assertThat(rule.canUpdate()).isTrue();
 
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
@@ -3935,8 +3862,10 @@
         // New ZenDeviceEffects is used; all fields considered set, since previously were null.
         assertThat(rule.getDeviceEffects()).isNotNull();
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
-        assertThat(rule.canUpdate()).isFalse();
-        assertThat(rule.getDeviceEffects().getUserModifiedFields()).isEqualTo(
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.canBeUpdatedByApp()).isFalse();
+        assertThat(storedRule.zenDeviceEffectsUserModifiedFields).isEqualTo(
                 ZenDeviceEffects.FIELD_GRAYSCALE);
     }
 
@@ -4339,7 +4268,6 @@
         String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
                 UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
         assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).getCreationTime()).isEqualTo(1000);
-        assertThat(mZenModeHelper.getAutomaticZenRule(ruleId).canUpdate()).isTrue();
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
@@ -4371,9 +4299,11 @@
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
         assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
                 ZenPolicy.STATE_ALLOW);
-        assertThat(finalRule.getUserModifiedFields()).isEqualTo(
+
+        ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(storedRule.userModifiedFields).isEqualTo(
                 AutomaticZenRule.FIELD_INTERRUPTION_FILTER);
-        assertThat(finalRule.getZenPolicy().getUserModifiedFields()).isEqualTo(
+        assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(
                 ZenPolicy.FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS);
 
         // Also, we discarded the "deleted rule" since we already used it for restoration.
@@ -4652,7 +4582,7 @@
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
-                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                                 null, true));
@@ -4672,12 +4602,75 @@
                 ZEN_MODE_ALARMS);
 
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
-                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, null, true));
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        String pkg = mContext.getPackageName();
+
+        // From app, call "setInterruptionFilter" and create and implicit rule.
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
+        assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
+                .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        // From user, update that rule's interruption filter.
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+
+        // From app, call "setInterruptionFilter" again.
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+                ZEN_MODE_NO_INTERRUPTIONS);
+
+        // The app's update was ignored, and the user's update is still current, and the current
+        // mode is the one they chose.
+        assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
+                .isEqualTo(ZEN_MODE_ALARMS);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        String pkg = mContext.getPackageName();
+
+        // From app, call "setInterruptionFilter" and create and implicit rule.
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+        String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
+        assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
+                .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+
+        // From user, update something in that rule, but not the interruption filter.
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
+                .setName("Renamed")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+
+        // From app, call "setInterruptionFilter" again.
+        mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(pkg, CUSTOM_PKG_UID,
+                ZEN_MODE_NO_INTERRUPTIONS);
+
+        // The app's update was accepted, and the current mode is the one that they wanted.
+        assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenMode)
+                .isEqualTo(ZEN_MODE_NO_INTERRUPTIONS);
+        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_NO_INTERRUPTIONS);
+    }
+
+    @Test
     public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
@@ -4747,8 +4740,7 @@
         Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy,
-                UPDATE_ORIGIN_APP);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, policy);
 
         ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
                 .disallowAllSounds()
@@ -4758,7 +4750,7 @@
                 .allowPriorityChannels(true)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
-                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                                 expectedZenPolicy, /* conditionActive= */ null));
@@ -4773,14 +4765,13 @@
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
         mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                original, UPDATE_ORIGIN_APP);
+                original);
 
         // Change priorityCallSenders: contacts -> starred.
         Policy updated = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_STARRED, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), CONVERSATION_SENDERS_IMPORTANT);
-        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated,
-                UPDATE_ORIGIN_APP);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, updated);
 
         ZenPolicy expectedZenPolicy = new ZenPolicy.Builder()
                 .disallowAllSounds()
@@ -4790,20 +4781,87 @@
                 .allowPriorityChannels(true)
                 .build();
         assertThat(mZenModeHelper.mConfig.automaticRules.values())
-                .comparingElementsUsing(IGNORE_TIMESTAMPS)
+                .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                                 expectedZenPolicy, /* conditionActive= */ null));
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        String pkg = mContext.getPackageName();
+
+        // From app, call "setNotificationPolicy" and create and implicit rule.
+        Policy originalPolicy = new Policy(PRIORITY_CATEGORY_MEDIA, 0, 0);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, originalPolicy);
+        String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
+
+        // From user, update that rule's policy.
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        ZenPolicy userUpdateZenPolicy = new ZenPolicy.Builder().disallowAllSounds()
+                .allowAlarms(true).build();
+        AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
+                .setZenPolicy(userUpdateZenPolicy)
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+
+        // From app, call "setNotificationPolicy" again.
+        Policy appUpdatePolicy = new Policy(PRIORITY_CATEGORY_SYSTEM, 0, 0);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, appUpdatePolicy);
+
+        // The app's update was ignored, and the user's update is still current.
+        assertThat(mZenModeHelper.mConfig.automaticRules.values())
+                .comparingElementsUsing(IGNORE_METADATA)
+                .containsExactly(
+                        expectedImplicitRule(pkg, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                userUpdateZenPolicy,
+                                /* conditionActive= */ null));
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() {
+        mZenModeHelper.mConfig.automaticRules.clear();
+        String pkg = mContext.getPackageName();
+
+        // From app, call "setNotificationPolicy" and create and implicit rule.
+        Policy originalPolicy = new Policy(PRIORITY_CATEGORY_MEDIA, 0, 0);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, originalPolicy);
+        String ruleId = getOnlyElement(mZenModeHelper.mConfig.automaticRules.keySet());
+
+        // From user, update something in that rule, but not the ZenPolicy.
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
+        AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
+                .setName("Rule renamed, not touching policy")
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdateRule, UPDATE_ORIGIN_USER, "reason",
+                Process.SYSTEM_UID);
+
+        // From app, call "setNotificationPolicy" again.
+        Policy appUpdatePolicy = new Policy(PRIORITY_CATEGORY_SYSTEM, 0, 0);
+        mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(pkg, CUSTOM_PKG_UID, appUpdatePolicy);
+
+        // The app's update was applied.
+        ZenPolicy appsSecondZenPolicy = new ZenPolicy.Builder()
+                .disallowAllSounds()
+                .allowSystem(true)
+                .allowPriorityChannels(true)
+                .build();
+        assertThat(getOnlyElement(mZenModeHelper.mConfig.automaticRules.values()).zenPolicy)
+                .isEqualTo(appsSecondZenPolicy);
+    }
+
+    @Test
     public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
         mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         withoutWtfCrash(
                 () -> mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME,
-                        CUSTOM_PKG_UID, new Policy(0, 0, 0), UPDATE_ORIGIN_APP));
+                        CUSTOM_PKG_UID, new Policy(0, 0, 0)));
 
         assertThat(mZenModeHelper.mConfig.automaticRules).isEmpty();
     }
@@ -4816,7 +4874,7 @@
                 Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
                 CONVERSATION_SENDERS_IMPORTANT);
         mZenModeHelper.applyGlobalPolicyAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                writtenPolicy, UPDATE_ORIGIN_APP);
+                writtenPolicy);
 
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
                 CUSTOM_PKG_NAME);
@@ -4856,7 +4914,7 @@
         assertThat(readPolicy.allowConversations()).isFalse();
     }
 
-    private static final Correspondence<ZenRule, ZenRule> IGNORE_TIMESTAMPS =
+    private static final Correspondence<ZenRule, ZenRule> IGNORE_METADATA =
             Correspondence.transforming(zr -> {
                 Parcel p = Parcel.obtain();
                 try {
@@ -4864,12 +4922,15 @@
                     p.setDataPosition(0);
                     ZenRule copy = new ZenRule(p);
                     copy.creationTime = 0;
+                    copy.userModifiedFields = 0;
+                    copy.zenPolicyUserModifiedFields = 0;
+                    copy.zenDeviceEffectsUserModifiedFields = 0;
                     return copy;
                 } finally {
                     p.recycle();
                 }
             },
-            "Ignoring timestamps");
+            "Ignoring timestamp and userModifiedFields");
 
     private ZenRule expectedImplicitRule(String ownerPkg, int zenMode, ZenPolicy policy,
             @Nullable Boolean conditionActive) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
index 7941eb4..4ed55df 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java
@@ -645,38 +645,12 @@
     }
 
     @Test
-    public void testFromParcel() {
-        ZenPolicy.Builder builder = new ZenPolicy.Builder();
-        builder.setUserModifiedFields(10);
-
-        ZenPolicy policy = builder.build();
-        assertThat(policy.getUserModifiedFields()).isEqualTo(10);
-
-        Parcel parcel = Parcel.obtain();
-        policy.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-
-        ZenPolicy fromParcel = ZenPolicy.CREATOR.createFromParcel(parcel);
-        assertThat(fromParcel.getUserModifiedFields()).isEqualTo(10);
-    }
-
-    @Test
-    public void testPolicy_userModifiedFields() {
-        ZenPolicy.Builder builder = new ZenPolicy.Builder();
-        builder.setUserModifiedFields(10);
-        assertThat(builder.build().getUserModifiedFields()).isEqualTo(10);
-
-        builder.setUserModifiedFields(0);
-        assertThat(builder.build().getUserModifiedFields()).isEqualTo(0);
-    }
-
-    @Test
     public void testPolicyBuilder_constructFromPolicy() {
         ZenPolicy.Builder builder = new ZenPolicy.Builder();
         ZenPolicy policy = builder.allowRepeatCallers(true).allowAlarms(false)
                 .showLights(true).showBadges(false)
                 .allowPriorityChannels(true)
-                .setUserModifiedFields(20).build();
+                .build();
 
         ZenPolicy newPolicy = new ZenPolicy.Builder(policy).build();
 
@@ -689,7 +663,6 @@
         assertThat(newPolicy.getVisualEffectPeek()).isEqualTo(ZenPolicy.STATE_UNSET);
 
         assertThat(newPolicy.getPriorityChannels()).isEqualTo(ZenPolicy.STATE_ALLOW);
-        assertThat(newPolicy.getUserModifiedFields()).isEqualTo(20);
     }
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
index 336bfdd..a8fd6f2 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
@@ -35,11 +35,13 @@
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.Keep;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.RingBuffer;
 import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
 
+import java.util.function.IntFunction;
+import java.util.function.Supplier;
+
 public class BroadcastResponseStatsLogger {
 
     private static final int MAX_LOG_SIZE =
@@ -49,10 +51,10 @@
 
     @GuardedBy("mLock")
     private final LogBuffer mBroadcastEventsBuffer = new LogBuffer(
-            BroadcastEvent.class, MAX_LOG_SIZE);
+            BroadcastEvent::new, BroadcastEvent[]::new, MAX_LOG_SIZE);
     @GuardedBy("mLock")
     private final LogBuffer mNotificationEventsBuffer = new LogBuffer(
-            NotificationEvent.class, MAX_LOG_SIZE);
+            NotificationEvent::new, NotificationEvent[]::new, MAX_LOG_SIZE);
 
     void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
             UserHandle targetUser, long idForResponseEvent,
@@ -96,8 +98,8 @@
 
     private static final class LogBuffer<T extends Data> extends RingBuffer<T> {
 
-        LogBuffer(Class<T> classType, int capacity) {
-            super(classType, capacity);
+        LogBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity) {
+            super(newItem, newBacking, capacity);
         }
 
         void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
@@ -179,8 +181,7 @@
         }
     }
 
-    @Keep
-    public static final class BroadcastEvent implements Data {
+    private static final class BroadcastEvent implements Data {
         public int sourceUid;
         public int targetUserId;
         public int targetUidProcessState;
@@ -200,8 +201,7 @@
         }
     }
 
-    @Keep
-    public static final class NotificationEvent implements Data {
+    private static final class NotificationEvent implements Data {
         public int type;
         public String packageName;
         public int userId;
@@ -218,7 +218,7 @@
         }
     }
 
-    public interface Data {
+    private interface Data {
         void reset();
     }
 }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
index 1df7012..49ad461 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
@@ -62,7 +62,8 @@
             SubscriptionManager subscriptionManager,
             TelephonyManager telephonyManager,
             Callback callback) {
-        mSubscriptionManager = Objects.requireNonNull(subscriptionManager);
+        mSubscriptionManager = Objects.requireNonNull(subscriptionManager)
+                .createForAllUserProfiles();
         mTelephonyManager = Objects.requireNonNull(telephonyManager);
         mCallback = Objects.requireNonNull(callback);
         mSubscriptionManager.addOnSubscriptionsChangedListener(
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 57b13e9..e81f482 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1049,8 +1049,17 @@
     public static final int PRESENTATION_UNAVAILABLE = 5;
 
 
+    /**
+     * Controls audio route for video calls.
+     * 0 - Use the default audio routing strategy.
+     * 1 - Disable the speaker. Route the audio to Headset or Bluetooth
+     *     or Earpiece, based on the default audio routing strategy.
+     * @hide
+     */
+    public static final String PROPERTY_VIDEOCALL_AUDIO_OUTPUT = "persist.radio.call.audio.output";
+
     /*
-     * Values for the adb property "persist.radio.videocall.audio.output"
+     * Values for the adb property "persist.radio.call.audio.output"
      */
     /** @hide */
     public static final int AUDIO_OUTPUT_ENABLE_SPEAKER = 0;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 73c26a3..1badf67 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9694,6 +9694,27 @@
             "remove_satellite_plmn_in_manual_network_scan_bool";
 
     /**
+     * An integer key holds the time interval for refreshing or re-querying the satellite
+     * entitlement status from the entitlement server to ensure it is the latest.
+     *
+     * The default value is 30 days (1 month).
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT =
+            "satellite_entitlement_status_refresh_days_int";
+
+    /**
+     * This configuration enables device to query the entitlement server to get the satellite
+     * configuration.
+     * This will need agreement the carrier before enabling this flag.
+     *
+     * The default value is false.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL =
+            "satellite_entitlement_supported_bool";
+
+    /**
      * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
      * the default APN (i.e. internet) will be used for tethering.
      *
@@ -10799,6 +10820,8 @@
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
         sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
+        sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 30);
+        sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 4c37f7d..b84ff29 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.annotation.FlaggedApi;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -46,6 +47,7 @@
 
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.lang.annotation.Retention;
@@ -152,12 +154,36 @@
     public static final long CAPABILITY_TERMINAL_BASED_CALL_WAITING = 1 << 2;
 
     /**
+     * This ImsService supports the capability to manage calls on multiple subscriptions at the same
+     * time.
+     * <p>
+     * When set, this ImsService supports managing calls on multiple subscriptions at the same time
+     * for all WLAN network configurations. Telephony will allow new outgoing/incoming IMS calls to
+     * be set up on other subscriptions while there is an ongoing call. The ImsService must also
+     * support managing calls on WWAN + WWAN configurations whenever the modem also reports
+     * simultaneous calling availability, which can be listened to using the
+     * {@link android.telephony.TelephonyCallback.SimultaneousCellularCallingSupportListener} API.
+     * Telephony will only allow additional ongoing/incoming IMS calls on another subscription to be
+     * set up on WWAN + WWAN configurations when the modem reports that simultaneous cellular
+     * calling is allowed at the current time on both subscriptions where there are ongoing calls.
+     * <p>
+     * When unset (default), this ImsService can not support calls on multiple subscriptions at the
+     * same time for any WLAN or WWAN configurations, so pending outgoing call placed on another
+     * cellular subscription while there is an ongoing call will be cancelled by Telephony.
+     * Similarly, any incoming call notification on another cellular subscription while there is an
+     * ongoing call will be rejected.
+     * @hide TODO: move this to system API when we have a backing implementation + CTS testing
+     */
+    @FlaggedApi(Flags.FLAG_SIMULTANEOUS_CALLING_INDICATIONS)
+    public static final long CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING = 1 << 3;
+
+    /**
      * Used for internal correctness checks of capabilities set by the ImsService implementation and
      * tracks the index of the largest defined flag in the capabilities long.
      * @hide
      */
     public static final long CAPABILITY_MAX_INDEX =
-            Long.numberOfTrailingZeros(CAPABILITY_TERMINAL_BASED_CALL_WAITING);
+            Long.numberOfTrailingZeros(CAPABILITY_SUPPORTS_SIMULTANEOUS_CALLING);
 
     /**
      * @hide
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 3b0397b..70047a6 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -928,10 +928,19 @@
     @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
     public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;
 
+    /**
+     * Satellite communication restricted by entitlement server. This can be triggered based on
+     * the EntitlementStatus value received from the entitlement server to enable or disable
+     * satellite.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2;
+
     /** @hide */
     @IntDef(prefix = "SATELLITE_COMMUNICATION_RESTRICTION_REASON_", value = {
             SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER,
-            SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION,
+            SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SatelliteCommunicationRestrictionReason {}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index e3988cd..c44d943 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -68,7 +68,7 @@
                         // This triggers a recalcuation of splitatributes.
                         ActivityEmbeddingController
                                 .getInstance(ActivityEmbeddingSecondaryActivity.this)
-                                .invalidateTopVisibleActivityStacks();
+                                .invalidateVisibleActivityStacks();
                     }
         });
         findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java
new file mode 100644
index 0000000..379c4ae
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/libcore-fake/com/android/okhttp/internalandroidapi/Dns.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.okhttp.internalandroidapi;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+
+/**
+ * A domain name service that resolves IP addresses for host names.
+ * @hide
+ * @hide This class is not part of the Android public SDK API
+ */
+public interface Dns {
+    /**
+     * Returns the IP addresses of {@code hostname}, in the order they should
+     * be attempted.
+     *
+     * @hide
+     */
+    List<InetAddress> lookup(String hostname) throws UnknownHostException;
+}