Merge "Add OWNERS file for libhostgraphics code" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2846221..bfe188d 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -12,171 +12,108 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-aconfig_srcjars = [
-    // !!! KEEP THIS LIST ALPHABETICAL !!!
-    ":aconfig_mediacodec_flags_java_lib{.generated_srcjars}",
-    ":android.adaptiveauth.flags-aconfig-java{.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.app.wearable.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.companion.virtualdevice.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.crashrecovery.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.codec-aconfig-java{.generated_srcjars}",
-    ":android.media.tv.flags-aconfig-java{.generated_srcjars}",
-    ":android.multiuser.flags-aconfig-java{.generated_srcjars}",
-    ":android.net.platform.flags-aconfig-java{.generated_srcjars}",
-    ":android.net.vcn.flags-aconfig-java{.generated_srcjars}",
-    ":android.net.wifi.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.service.appprediction.flags-aconfig-java{.generated_srcjars}",
-    ":android.service.voice.flags-aconfig-java{.generated_srcjars}",
-    ":android.speech.flags-aconfig-java{.generated_srcjars}",
-    ":android.systemserver.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",
-    ":backup_flags_lib{.generated_srcjars}",
-    ":camera_platform_flags_core_java_lib{.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.internal.pm.pkg.component.flags-aconfig-java{.generated_srcjars}",
-    ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
-    ":com.android.media.flags.editing-aconfig-java{.generated_srcjars}",
-    ":com.android.media.flags.projection-aconfig-java{.generated_srcjars}",
-    ":com.android.net.thread.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}",
-    // !!! KEEP THIS LIST ALPHABETICAL !!!
-]
-
-stubs_defaults {
+aconfig_declarations_group {
     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.crashrecovery.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.platform.flags-aconfig",
-        "android.net.vcn.flags-aconfig",
-        "android.net.wifi.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.appprediction.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.thread.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",
+    aconfig_declarations_groups: [
+        "audio-framework-aconfig",
+    ],
+    java_aconfig_libraries: [
+        // !!! KEEP THIS LIST ALPHABETICAL !!!
+        "aconfig_mediacodec_flags_java_lib",
+        "android.adaptiveauth.flags-aconfig-java",
+        "android.app.flags-aconfig-java",
+        "android.app.smartspace.flags-aconfig-java",
+        "android.app.usage.flags-aconfig-java",
+        "android.app.wearable.flags-aconfig-java",
+        "android.appwidget.flags-aconfig-java",
+        "android.chre.flags-aconfig-java",
+        "android.companion.flags-aconfig-java",
+        "android.companion.virtual.flags-aconfig-java",
+        "android.companion.virtualdevice.flags-aconfig-java",
+        "android.content.flags-aconfig-java",
+        "android.content.pm.flags-aconfig-java",
+        "android.content.res.flags-aconfig-java",
+        "android.crashrecovery.flags-aconfig-java",
+        "android.credentials.flags-aconfig-java",
+        "android.database.sqlite-aconfig-java",
+        "android.hardware.biometrics.flags-aconfig-java",
+        "android.hardware.devicestate.feature.flags-aconfig-java",
+        "android.hardware.flags-aconfig-java",
+        "android.hardware.radio.flags-aconfig-java",
+        "android.hardware.usb.flags-aconfig-java",
+        "android.location.flags-aconfig-java",
+        "android.media.codec-aconfig-java",
+        "android.media.tv.flags-aconfig-java",
+        "android.multiuser.flags-aconfig-java",
+        "android.net.platform.flags-aconfig-java",
+        "android.net.vcn.flags-aconfig-java",
+        "android.net.wifi.flags-aconfig-java",
+        "android.nfc.flags-aconfig-java",
+        "android.os.flags-aconfig-java",
+        "android.os.vibrator.flags-aconfig-java",
+        "android.permission.flags-aconfig-java",
+        "android.provider.flags-aconfig-java",
+        "android.security.flags-aconfig-java",
+        "android.server.app.flags-aconfig-java",
+        "android.service.autofill.flags-aconfig-java",
+        "android.service.chooser.flags-aconfig-java",
+        "android.service.controls.flags-aconfig-java",
+        "android.service.dreams.flags-aconfig-java",
+        "android.service.notification.flags-aconfig-java",
+        "android.service.appprediction.flags-aconfig-java",
+        "android.service.voice.flags-aconfig-java",
+        "android.speech.flags-aconfig-java",
+        "android.systemserver.flags-aconfig-java",
+        "android.tracing.flags-aconfig-java",
+        "android.view.accessibility.flags-aconfig-java",
+        "android.view.contentcapture.flags-aconfig-java",
+        "android.view.contentprotection.flags-aconfig-java",
+        "android.view.flags-aconfig-java",
+        "android.view.inputmethod.flags-aconfig-java",
+        "android.webkit.flags-aconfig-java",
+        "android.widget.flags-aconfig-java",
+        "backup_flags_lib",
+        "camera_platform_flags_core_java_lib",
+        "com.android.hardware.input-aconfig-java",
+        "com.android.input.flags-aconfig-java",
+        "com.android.internal.foldables.flags-aconfig-java",
+        "com.android.internal.pm.pkg.component.flags-aconfig-java",
+        "com.android.media.flags.bettertogether-aconfig-java",
+        "com.android.media.flags.editing-aconfig-java",
+        "com.android.media.flags.projection-aconfig-java",
+        "com.android.net.thread.flags-aconfig-java",
+        "com.android.server.flags.services-aconfig-java",
+        "com.android.text.flags-aconfig-java",
+        "com.android.window.flags.window-aconfig-java",
+        "device_policy_aconfig_flags_lib",
+        "display_flags_lib",
+        "framework-jobscheduler-job.flags-aconfig-java",
+        "framework_graphics_flags_java_lib",
+        "hwui_flags_java_lib",
+        "power_flags_lib",
+        "sdk_sandbox_flags_lib",
+        "surfaceflinger_flags_java_lib",
+        "telecom_flags_core_java_lib",
+        "telephony_flags_core_java_lib",
+        // !!! KEEP THIS LIST ALPHABETICAL !!!
     ],
 }
 
 filegroup {
     name: "framework-minus-apex-aconfig-srcjars",
-    srcs: aconfig_srcjars,
+    srcs: [
+        ":framework-minus-apex-aconfig-declarations{.srcjars}",
+    ],
 }
 
 // Aconfig declarations and libraries for the core framework
 java_defaults {
     name: "framework-minus-apex-aconfig-libraries",
     // Add java_aconfig_libraries to here to add them to the core framework
-    srcs: aconfig_srcjars,
     // Add aconfig-annotations-lib as a dependency for the optimization
+    srcs: [
+        ":framework-minus-apex-aconfig-declarations{.srcjars}",
+    ],
     libs: ["aconfig-annotations-lib"],
 }
 
@@ -227,6 +164,19 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// DeviceStateManager
+aconfig_declarations {
+    name: "android.hardware.devicestate.feature.flags-aconfig",
+    package: "android.hardware.devicestate.feature.flags",
+    srcs: ["core/java/android/hardware/devicestate/feature/*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.hardware.devicestate.feature.flags-aconfig-java",
+    aconfig_declarations: "android.hardware.devicestate.feature.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Input
 aconfig_declarations {
     name: "com.android.hardware.input.input-aconfig",
diff --git a/api/Android.bp b/api/Android.bp
index 9d2147c..eeb76fb 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -302,7 +302,7 @@
 // classpath (or sources) somehow.
 stubs_defaults {
     name: "android-non-updatable-stubs-defaults",
-    defaults: ["framework-minus-apex-aconfig-declarations"],
+    aconfig_declarations: ["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 4af2c52..e8a342d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24567,6 +24567,7 @@
     method @FlaggedApi("com.android.media.flags.enable_screen_off_scanning") @NonNull public android.media.MediaRouter2.ScanToken requestScan(@NonNull android.media.MediaRouter2.ScanRequest);
     method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
     method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
+    method @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") @RequiresPermission(anyOf={android.Manifest.permission.MEDIA_CONTENT_CONTROL, android.Manifest.permission.MEDIA_ROUTING_CONTROL}) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
     method public boolean showSystemOutputSwitcher();
     method public void stop();
     method public void transferTo(@NonNull android.media.MediaRoute2Info);
@@ -52257,6 +52258,7 @@
     method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
     method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
     method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int);
+    method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") @NonNull public android.view.SurfaceControl.Transaction setDesiredHdrHeadroom(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0f) float);
     method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setDesiredPresentTimeNanos(long);
     method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float);
     method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
@@ -52360,6 +52362,7 @@
     method @Nullable public android.os.IBinder getHostToken();
     method public android.view.SurfaceControl getSurfaceControl();
     method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
+    method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
     method public void setSecure(boolean);
     method public void setSurfaceLifecycle(int);
     method public void setZOrderMediaOverlay(boolean);
@@ -60249,6 +60252,7 @@
   }
 
   @FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions {
+    method @FlaggedApi("android.appwidget.flags.draw_data_parcel") public static long getSupportedVersion();
   }
 
   @FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions.Builder {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index af5ea21..d8ba84d 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -367,7 +367,7 @@
     field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
-    field @FlaggedApi("android.service.chooser.support_nfc_resolver") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
     field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
@@ -4827,7 +4827,7 @@
   }
 
   @FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface SessionProcessor.CaptureCallback {
-    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull android.hardware.camera2.CaptureResult);
+    method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull java.util.Map<android.hardware.camera2.CaptureResult.Key,java.lang.Object>);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int, int);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProcessStarted(int);
     method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int);
@@ -4851,6 +4851,39 @@
 
 }
 
+package android.hardware.devicestate {
+
+  @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+    method @IntRange(from=0x0) public int getIdentifier();
+    method @NonNull public String getName();
+    method public boolean hasProperties(@NonNull int...);
+    method public boolean hasProperty(int);
+    field public static final int PROPERTY_EMULATED_ONLY = 10; // 0xa
+    field public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15; // 0xf
+    field public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17; // 0x11
+    field public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16; // 0x10
+    field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12; // 0xc
+    field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11; // 0xb
+    field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1; // 0x1
+    field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2; // 0x2
+    field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3; // 0x3
+    field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13; // 0xd
+    field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14; // 0xe
+  }
+
+  @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
+    method @NonNull public java.util.List<android.hardware.devicestate.DeviceState> getSupportedDeviceStates();
+    method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+    method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+  }
+
+  public static interface DeviceStateManager.DeviceStateCallback {
+    method public default void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
+    method public default void onSupportedStatesChanged(@NonNull java.util.List<android.hardware.devicestate.DeviceState>);
+  }
+
+}
+
 package android.hardware.display {
 
   public final class AmbientBrightnessDayStats implements android.os.Parcelable {
@@ -7125,7 +7158,6 @@
   public final class MediaRouter2 {
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
     method @Nullable public String getClientPackageName();
-    method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
     method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan();
     method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan();
     method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
@@ -12329,14 +12361,6 @@
 
 }
 
-package android.service.chooser {
-
-  @FlaggedApi("android.service.chooser.support_nfc_resolver") public class CustomChoosers {
-    method @FlaggedApi("android.service.chooser.support_nfc_resolver") @NonNull public static android.content.Intent createNfcResolverIntent(@NonNull android.content.Intent, @Nullable CharSequence, @NonNull java.util.List<android.content.pm.ResolveInfo>);
-  }
-
-}
-
 package android.service.cloudsearch {
 
   public abstract class CloudSearchService extends android.app.Service {
@@ -13390,43 +13414,6 @@
     method @NonNull public android.service.voice.HotwordRejectedResult.Builder setConfidenceLevel(int);
   }
 
-  @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public final class HotwordTrainingAudio implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.media.AudioFormat getAudioFormat();
-    method @NonNull public int getAudioType();
-    method @NonNull public byte[] getHotwordAudio();
-    method public int getHotwordOffsetMillis();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingAudio> CREATOR;
-    field public static final int HOTWORD_OFFSET_UNSET = -1; // 0xffffffff
-  }
-
-  @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final class HotwordTrainingAudio.Builder {
-    ctor public HotwordTrainingAudio.Builder(@NonNull byte[], @NonNull android.media.AudioFormat);
-    method @NonNull public android.service.voice.HotwordTrainingAudio build();
-    method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioFormat(@NonNull android.media.AudioFormat);
-    method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioType(@NonNull int);
-    method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte[]);
-    method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordOffsetMillis(int);
-  }
-
-  @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public final class HotwordTrainingData implements android.os.Parcelable {
-    method public int describeContents();
-    method public static int getMaxTrainingDataBytes();
-    method public int getTimeoutStage();
-    method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudioList();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingData> CREATOR;
-  }
-
-  @FlaggedApi("android.service.voice.flags.allow_training_data_egress_from_hds") public static final class HotwordTrainingData.Builder {
-    ctor public HotwordTrainingData.Builder();
-    method @NonNull public android.service.voice.HotwordTrainingData.Builder addTrainingAudio(@NonNull android.service.voice.HotwordTrainingAudio);
-    method @NonNull public android.service.voice.HotwordTrainingData build();
-    method @NonNull public android.service.voice.HotwordTrainingData.Builder setTimeoutStage(int);
-    method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudioList(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>);
-  }
-
   public interface SandboxedDetectionInitializer {
     method public static int getMaxCustomInitializationStatus();
     method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f6366a2..334d9e6 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1620,22 +1620,26 @@
 
 package android.hardware.devicestate {
 
-  public final class DeviceStateManager {
+  @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+    ctor @Deprecated public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, int);
+    ctor public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, @NonNull java.util.Set<java.lang.Integer>);
+    field public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8; // 0x8
+  }
+
+  @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
     method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
-    method @NonNull public int[] getSupportedStates();
-    method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+    method @Deprecated @NonNull public int[] getSupportedStates();
     method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
     method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
-    method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
-    field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
-    field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
+    field public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000; // 0x2710
+    field public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0; // 0x0
   }
 
   public static interface DeviceStateManager.DeviceStateCallback {
-    method public default void onBaseStateChanged(int);
-    method public void onStateChanged(int);
-    method public default void onSupportedStatesChanged(@NonNull int[]);
+    method @Deprecated public default void onBaseStateChanged(int);
+    method @Deprecated public void onStateChanged(int);
+    method @Deprecated public default void onSupportedStatesChanged(@NonNull int[]);
   }
 
   public final class DeviceStateRequest {
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d743992..91baa4e 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -707,7 +707,9 @@
      * Only components from the same {@link ComponentName#getPackageName package} as the calling app
      * are allowed.
      *
-     * Your app must have an association with a device before calling this API
+     * Your app must have an association with a device before calling this API.
+     *
+     * Side-loaded apps must allow restricted settings before requesting notification access.
      *
      * <p>Calling this API requires a uses-feature
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
@@ -721,6 +723,9 @@
             IntentSender intentSender = mService
                     .requestNotificationAccess(component, mContext.getUserId())
                     .getIntentSender();
+            if (intentSender == null) {
+                return;
+            }
             mContext.startIntentSender(intentSender, null, 0, 0, 0,
                     ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
                             ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index d8594e5..2e428e5 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureFailure;
@@ -184,8 +183,8 @@
          *                   capture results. This is the return value of
          *                   either {@link #startRepeating} or {@link
          *                   #startMultiFrameCapture}.
-         * @param results  The supported capture results. Do note
-         *                  that if results 'android.jpeg.quality' and
+         * @param results   Key value map of the supported capture results.
+         *                  Do note that if results 'android.jpeg.quality' and
          *                  android.jpeg.orientation' are present in the
          *                  process capture input results, then the values
          *                  must also be passed as part of this callback.
@@ -195,7 +194,7 @@
          */
         @FlaggedApi(Flags.FLAG_CONCERT_MODE)
         void onCaptureCompleted(long shutterTimestamp, int requestId,
-                @NonNull CaptureResult results);
+                @NonNull Map<CaptureResult.Key, Object> results);
     }
 
     /**
@@ -415,7 +414,7 @@
         public int startRepeating(ICaptureCallback callback) throws RemoteException {
             return SessionProcessor.this.startRepeating(
                     new HandlerExecutor(new Handler(Looper.getMainLooper())),
-                    new CaptureCallbackImpl(callback));
+                    new CaptureCallbackImpl(callback, mVendorId));
         }
 
         @Override
@@ -428,7 +427,7 @@
                 throws RemoteException {
             return SessionProcessor.this.startMultiFrameCapture(
                     new HandlerExecutor(new Handler(Looper.getMainLooper())),
-                    new CaptureCallbackImpl(callback));
+                    new CaptureCallbackImpl(callback, mVendorId));
         }
 
         @Override
@@ -441,7 +440,7 @@
                 throws RemoteException {
             return SessionProcessor.this.startTrigger(captureRequest,
                     new HandlerExecutor(new Handler(Looper.getMainLooper())),
-                    new CaptureCallbackImpl(callback));
+                    new CaptureCallbackImpl(callback, mVendorId));
         }
 
         @Override
@@ -453,9 +452,11 @@
 
     private static final class CaptureCallbackImpl implements CaptureCallback {
         private final ICaptureCallback mCaptureCallback;
+        private long mVendorId = -1;
 
-        CaptureCallbackImpl(@NonNull ICaptureCallback cb) {
+        CaptureCallbackImpl(@NonNull ICaptureCallback cb, long vendorId) {
             mCaptureCallback = cb;
+            mVendorId = vendorId;
         }
 
         @Override
@@ -505,10 +506,14 @@
 
         @Override
         public void onCaptureCompleted(long shutterTimestamp, int requestId,
-                @androidx.annotation.NonNull CaptureResult results) {
+                Map<CaptureResult.Key, Object> results) {
+            CameraMetadataNative captureResults = new CameraMetadataNative();
+            captureResults.setVendorId(mVendorId);
+            for (Map.Entry<CaptureResult.Key, Object> entry : results.entrySet()) {
+                captureResults.set(entry.getKey(), entry.getValue());
+            }
             try {
-                mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId,
-                        results.getNativeCopy());
+                mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId, captureResults);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to notify capture complete due to remote exception!");
             }
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index 5a34905..8629354 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -16,18 +16,25 @@
 
 package android.hardware.devicestate;
 
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collections;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * A state of the device defined by the {@link DeviceStateProvider} and managed by the
@@ -37,21 +44,29 @@
  * state of the system. This is useful for variable-state devices, like foldable or rollable
  * devices, that can be configured by users into differing hardware states, which each may have a
  * different expected use case.
- * @hide
  *
+ * @hide
  * @see DeviceStateManager
  */
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
 public final class DeviceState {
     /**
      * Flag that indicates override requests should be cancelled when this device state becomes the
      * base device state.
+     * @hide
+     * @deprecated use {@link #PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS}
      */
+    @Deprecated
     public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
 
     /**
      * Flag that indicates this device state is inaccessible for applications to be placed in. This
-     * could be a device-state where the {@link DEFAULT_DISPLAY} is not enabled.
+     * could be a device-state where the {@link Display#DEFAULT_DISPLAY} is not enabled.
+     * @hide
+     * @deprecated use {@link #PROPERTY_APP_INACCESSIBLE}
      */
+    @Deprecated
     public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
 
     /**
@@ -60,7 +75,10 @@
      * through emulation and have no physical configuration to match.
      *
      * This flag indicates that the corresponding state can only be entered through emulation.
+     * @hide
+     * @deprecated use {@link #PROPERTY_EMULATED_ONLY}
      */
+    @Deprecated
     public static final int FLAG_EMULATED_ONLY = 1 << 2;
 
     /**
@@ -68,19 +86,28 @@
      * requesting app is no longer on top. The app is considered not on top when (1) the top
      * activity in the system is from a different app, (2) the device is in sleep mode, or
      * (3) the keyguard shows up.
+     * @hide
+     * @deprecated use {@link #PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP}
      */
+    @Deprecated
     public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;
 
     /**
      * This flag indicates that the corresponding state should be disabled when the device is
      * overheating and reaching the critical status.
+     * @hide
+     * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL}
      */
+    @Deprecated
     public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
 
     /**
      * This flag indicates that the corresponding state should be disabled when power save mode
      * is enabled.
+     * @hide
+     * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE}
      */
+    @Deprecated
     public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
 
     /** @hide */
@@ -92,11 +119,157 @@
             FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
             FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
     })
+    @Deprecated
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceStateFlags {}
 
+    /**
+     * Property that indicates that a fold-in style foldable device is currently in a fully closed
+     * configuration.
+     */
+    public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1;
+
+    /**
+     * Property that indicates that a fold-in style foldable device is currently in a half-opened
+     * configuration. This signifies that the device's hinge is positioned somewhere around 90
+     * degrees. Checking for display configuration properties as well can provide information
+     * on which display is currently active.
+     */
+    public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2;
+
+    /**
+     * Property that indicates that a fold-in style foldable device is currently in a fully open
+     * configuration.
+     */
+    public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3;
+
+    /**
+     * Property that indicates override requests should be cancelled when the device is physically
+     * put into this state.
+     * @hide
+     */
+    public static final int PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS = 4;
+
+    /**
+     * This property indicates that the corresponding state should be automatically canceled when
+     * the requesting app is no longer on top. The app is considered not on top when (1) the top
+     * activity in the system is from a different app, (2) the device is in sleep mode, or
+     * (3) the keyguard shows up.
+     * @hide
+     */
+    public static final int PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 5;
+
+    /**
+     * This property indicates that the corresponding state should be disabled when the device is
+     * overheating and reaching the critical status.
+     * @hide
+     */
+    public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 6;
+
+    /**
+     * This property indicates that the corresponding state should be disabled when power save mode
+     * is enabled.
+     * @hide
+     */
+    public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 7;
+
+    /**
+     * This property denotes that this state is available for applications to request and the system
+     * server should deny any request that comes from a process that does not hold the
+     * CONTROL_DEVICE_STATE permission if it is requesting a state that does not have this property
+     * on it.
+     * @hide
+     */
+    @TestApi
+    public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8;
+
+    /**
+     * Property that indicates this device state is inaccessible for applications to be made
+     * visible to the user. This could be a device-state where the {@link Display#DEFAULT_DISPLAY}
+     * is not enabled.
+     * @hide
+     */
+    public static final int PROPERTY_APP_INACCESSIBLE = 9;
+
+    /**
+     * This property indidcates that this state can only be entered through emulation and has no
+     * physical configuration to match.
+     */
+    public static final int PROPERTY_EMULATED_ONLY = 10;
+
+    /**
+     * Property that indicates that the outer display area of a foldable device is currently the
+     * primary display area.
+     *
+     * Note: This does not necessarily mean that the outer display area is the
+     * @link Display#DEFAULT_DISPLAY}.
+     */
+    public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11;
+
+    /**
+     * Property that indicates that the inner display area of a foldable device is currently the
+     * primary display area.
+     *
+     * Note: This does not necessarily mean that the inner display area is the
+     * {@link Display#DEFAULT_DISPLAY}.
+     */
+    public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12;
+
+    /**
+     * Property that indicates that this device state will attempt to trigger the device to go to
+     * sleep.
+     */
+    public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13;
+
+    /**
+     * Property that indicates that this device state will attempt to trigger the device to wake up.
+     */
+    public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14;
+
+    /**
+     * Property that indicates that an external display has been connected to the device. Specifics
+     * around display mode or properties around the display should be gathered through
+     * {@link android.hardware.display.DisplayManager}
+     */
+    public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15;
+    /**
+     * Property that indicates that this state corresponds to the device state for rear display
+     * mode. This means that the active display is facing the same direction as the rear camera.
+     */
+    public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16;
+
+    /**
+     * Property that indicates that this state corresponds to the device state where both displays
+     * on a foldable are active, with the internal display being the default display.
+     */
+    public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17;
+
+    /** @hide */
+    @IntDef(prefix = {"PROPERTY_"}, flag = true, value = {
+            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+            PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+            PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
+            PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
+            PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
+            PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+            PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST,
+            PROPERTY_APP_INACCESSIBLE,
+            PROPERTY_EMULATED_ONLY,
+            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+            PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+            PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
+            PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
+            PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY,
+            PROPERTY_FEATURE_REAR_DISPLAY,
+            PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+    public @interface DeviceStateProperties {}
+
     /** Unique identifier for the device state. */
-    @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+    @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
     private final int mIdentifier;
 
     /** String description of the device state. */
@@ -106,20 +279,49 @@
     @DeviceStateFlags
     private final int mFlags;
 
+    private final Set<@DeviceStateProperties Integer> mProperties;
+
+    /**
+     * @deprecated Deprecated in favor of {@link #DeviceState(int, String, Set)}
+     * @hide
+     */
+    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+    @TestApi
+    @Deprecated
     public DeviceState(
-            @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+            @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                    MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
             @NonNull String name,
             @DeviceStateFlags int flags) {
-        Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
+        Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+                MAXIMUM_DEVICE_STATE_IDENTIFIER,
                 "identifier");
 
         mIdentifier = identifier;
         mName = name;
         mFlags = flags;
+        mProperties = Collections.emptySet();
+    }
+
+    /** @hide */
+    @TestApi
+    public DeviceState(
+            @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                    MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
+            @NonNull String name,
+            @NonNull Set<@DeviceStateProperties Integer> properties) {
+        Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+                MAXIMUM_DEVICE_STATE_IDENTIFIER,
+                "identifier");
+
+        mIdentifier = identifier;
+        mName = name;
+        mProperties = Set.copyOf(properties);
+        mFlags = 0;
     }
 
     /** Returns the unique identifier for the device state. */
-    @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+    @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER)
     public int getIdentifier() {
         return mIdentifier;
     }
@@ -130,6 +332,12 @@
         return mName;
     }
 
+    /**
+     * @hide
+     * @deprecated in favor of {@link #hasProperty(int)} method
+     */
+    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+    @Deprecated
     @DeviceStateFlags
     public int getFlags() {
         return mFlags;
@@ -138,9 +346,9 @@
     @Override
     public String toString() {
         return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\''
-                + ", app_accessible=" + !hasFlag(FLAG_APP_INACCESSIBLE)
+                + ", app_accessible=" + !hasProperty(PROPERTY_APP_INACCESSIBLE)
                 + ", cancel_when_requester_not_on_top="
-                + hasFlag(FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
+                + hasProperty(PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
                 + "}";
     }
 
@@ -151,17 +359,40 @@
         DeviceState that = (DeviceState) o;
         return mIdentifier == that.mIdentifier
                 && Objects.equals(mName, that.mName)
-                && mFlags == that.mFlags;
+                && Objects.equals(mProperties, that.mProperties);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIdentifier, mName, mFlags);
+        return Objects.hash(mIdentifier, mName, mProperties);
     }
 
     /** Checks if a specific flag is set
+     * @hide
+     * @deprecated in favor of {@link #hasProperty(int)}
      */
+    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+    @Deprecated
     public boolean hasFlag(int flagToCheckFor) {
         return (mFlags & flagToCheckFor) == flagToCheckFor;
     }
+
+    /**
+     * Checks if a specific property is set on this state
+     */
+    public boolean hasProperty(@DeviceStateProperties int propertyToCheckFor) {
+        return mProperties.contains(propertyToCheckFor);
+    }
+
+    /**
+     * Checks if a list of properties are all set on this state
+     */
+    public boolean hasProperties(@NonNull @DeviceStateProperties int... properties) {
+        for (int i = 0; i < properties.length; i++) {
+            if (mProperties.contains(properties[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 6a667fe..8b4d43e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -18,15 +18,19 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -36,7 +40,8 @@
  *
  * @hide
  */
-@TestApi
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
 @SystemService(Context.DEVICE_STATE_SERVICE)
 public final class DeviceStateManager {
     /**
@@ -46,11 +51,19 @@
      */
     public static final int INVALID_DEVICE_STATE = -1;
 
-    /** The minimum allowed device state identifier. */
-    public static final int MINIMUM_DEVICE_STATE = 0;
+    /**
+     * The minimum allowed device state identifier.
+     * @hide
+     */
+    @TestApi
+    public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0;
 
-    /** The maximum allowed device state identifier. */
-    public static final int MAXIMUM_DEVICE_STATE = 255;
+    /**
+     * The maximum allowed device state identifier.
+     * @hide
+     */
+    @TestApi
+    public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000;
 
     /**
      * Intent needed to launch the rear display overlay activity from SysUI
@@ -83,13 +96,27 @@
     /**
      * Returns the list of device states that are supported and can be requested with
      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     * @deprecated use {@link #getSupportedDeviceStates()}
+     * @hide
      */
+    // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+    @TestApi
+    @Deprecated
     @NonNull
     public int[] getSupportedStates() {
         return mGlobal.getSupportedStates();
     }
 
     /**
+     * Returns the list of device states that are supported and can be requested with
+     * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+     */
+    @NonNull
+    public List<DeviceState> getSupportedDeviceStates() {
+        return mGlobal.getSupportedDeviceStates();
+    }
+
+    /**
      * Submits a {@link DeviceStateRequest request} to modify the device state.
      * <p>
      * By default, the request is kept active until one of the following occurs:
@@ -107,7 +134,10 @@
      * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
      *
      * @see DeviceStateRequest
+     * @hide
      */
+    @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+    @TestApi
     @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
             conditional = true)
     public void requestState(@NonNull DeviceStateRequest request,
@@ -124,7 +154,10 @@
      *
      * @throws SecurityException if the caller is neither the current top-focused activity nor if
      * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
+     * @hide
      */
+    @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+    @TestApi
     @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
             conditional = true)
     public void cancelStateRequest() {
@@ -151,11 +184,11 @@
      * emulated override requests take priority.
      *
      * @throws IllegalArgumentException if the requested state is unsupported.
-     * @throws SecurityException if the caller does not hold the
-     * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
      *
      * @see DeviceStateRequest
+     * @hide
      */
+    @TestApi
     @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
     public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
             @Nullable @CallbackExecutor Executor executor,
@@ -169,9 +202,9 @@
      * <p>
      * This method is noop if there is no base state request currently active.
      *
-     * @throws SecurityException if the caller does not hold the
-     * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+     * @hide
      */
+    @TestApi
     @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
     public void cancelBaseStateOverride() {
         mGlobal.cancelBaseStateOverride();
@@ -209,10 +242,31 @@
          * @param supportedStates the new supported states.
          *
          * @see DeviceStateManager#getSupportedStates()
+         * @deprecated use {@link #onSupportedStatesChanged(List)}
+         * @hide
          */
+        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+        @TestApi
+        @Deprecated
         default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
 
         /**
+         * Called in response to a change in the states supported by the device.
+         * <p>
+         * Guaranteed to be called once on registration of the callback with the initial value and
+         * then on every subsequent change in the supported states.
+         *
+         * The supported device states may change due to certain states becoming unavailable
+         * due to device configuration or device conditions such as if the device is too hot or
+         * external monitors have been connected.
+         *
+         * @param supportedStates the new supported states.
+         *
+         * @see DeviceStateManager#getSupportedDeviceStates()
+         */
+        default void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {}
+
+        /**
          * Called in response to a change in the base device state.
          * <p>
          * The base state is the state of the device without considering any requests made through
@@ -224,7 +278,13 @@
          * then on every subsequent change in the non-override state.
          *
          * @param state the new base device state.
+         * @deprecated use {@link #onDeviceStateChanged(DeviceState)} and query for physical
+         * properties that are relevant to your needs.
+         * @hide
          */
+        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+        @TestApi
+        @Deprecated
         default void onBaseStateChanged(int state) {}
 
         /**
@@ -234,8 +294,24 @@
          * then on every subsequent change in device state.
          *
          * @param state the new device state.
+         * @deprecated use {@link #onDeviceStateChanged(DeviceState)}
+         * @hide
          */
+        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+        @TestApi
+        @Deprecated
         void onStateChanged(int state);
+
+        /**
+         * Called in response to device state changes.
+         * <p>
+         * Guaranteed to be called once on registration of the callback with the initial value and
+         * then on every subsequent change in device state.
+         *
+         * @param state the new device state.
+         */
+        // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+        default void onDeviceStateChanged(@NonNull DeviceState state) {}
     }
 
     /**
@@ -247,6 +323,7 @@
     public static class FoldStateListener implements DeviceStateCallback {
         private final int[] mFoldedDeviceStates;
         private final Consumer<Boolean> mDelegate;
+        private final android.hardware.devicestate.feature.flags.FeatureFlags mFeatureFlags;
 
         @Nullable
         private Boolean lastResult;
@@ -259,11 +336,23 @@
             mFoldedDeviceStates = context.getResources().getIntArray(
                     com.android.internal.R.array.config_foldedDeviceStates);
             mDelegate = listener;
+            mFeatureFlags = new android.hardware.devicestate.feature.flags.FeatureFlagsImpl();
         }
 
         @Override
-        public final void onStateChanged(int state) {
-            final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+        public final void onStateChanged(int state) {}
+
+        @Override
+        public final void onDeviceStateChanged(@NonNull DeviceState deviceState) {
+            final boolean folded;
+            if (mFeatureFlags.deviceStatePropertyApi()) {
+                // TODO(b/325124054): Update when system server refactor is completed
+                folded = deviceState.hasProperty(
+                        DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+                        || ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+            } else {
+                folded = ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+            }
 
             if (lastResult == null || !lastResult.equals(folded)) {
                 lastResult = folded;
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c379188..6db5aee 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.os.Binder;
@@ -32,9 +33,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
@@ -50,12 +55,18 @@
     private static final String TAG = "DeviceStateManagerGlobal";
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
+    // TODO(b/325124054): Remove when system server refactor is completed
+    private static int[] sFoldedDeviceStates = new int[0];
+
     /**
      * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
      * connection with the device state service couldn't be established.
      */
     @Nullable
     public static DeviceStateManagerGlobal getInstance() {
+        // TODO(b/325124054): Remove when system server refactor is completed
+        instantiateFoldedStateArray();
+
         synchronized (DeviceStateManagerGlobal.class) {
             if (sInstance == null) {
                 IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
@@ -68,6 +79,16 @@
         }
     }
 
+    // TODO(b/325124054): Remove when system server refactor is completed
+    // TODO(b/325330654): Investigate if we need a Context passed in to DSMGlobal
+    private static void instantiateFoldedStateArray() {
+        Context context = ActivityThread.currentApplication();
+        if (context != null) {
+            sFoldedDeviceStates = context.getResources().getIntArray(
+                    com.android.internal.R.array.config_foldedDeviceStates);
+        }
+    }
+
     private final Object mLock = new Object();
     @NonNull
     private final IDeviceStateManager mDeviceStateManager;
@@ -116,6 +137,32 @@
     }
 
     /**
+     * Returns the {@link List} of supported device states.
+     *
+     * @see DeviceStateManager#getSupportedDeviceStates()
+     */
+    public List<DeviceState> getSupportedDeviceStates() {
+        synchronized (mLock) {
+            final DeviceStateInfo currentInfo;
+            if (mLastReceivedInfo != null) {
+                // If we have mLastReceivedInfo a callback is registered for this instance and it
+                // is receiving the most recent info from the server. Use that info here.
+                currentInfo = mLastReceivedInfo;
+            } else {
+                // If mLastReceivedInfo is null there is no registered callback so we manually
+                // fetch the current info.
+                try {
+                    currentInfo = mDeviceStateManager.getDeviceStateInfo();
+                } catch (RemoteException ex) {
+                    throw ex.rethrowFromSystemServer();
+                }
+            }
+
+            return createDeviceStateList(currentInfo.supportedStates);
+        }
+    }
+
+    /**
      * Submits a {@link DeviceStateRequest request} to modify the device state.
      *
      * @see DeviceStateManager#requestState(DeviceStateRequest, Executor,
@@ -241,8 +288,10 @@
                 final int[] supportedStates = Arrays.copyOf(mLastReceivedInfo.supportedStates,
                         mLastReceivedInfo.supportedStates.length);
                 wrapper.notifySupportedStatesChanged(supportedStates);
+                wrapper.notifySupportedDeviceStatesChanged(createDeviceStateList(supportedStates));
                 wrapper.notifyBaseStateChanged(mLastReceivedInfo.baseState);
                 wrapper.notifyStateChanged(mLastReceivedInfo.currentState);
+                wrapper.notifyDeviceStateChanged(createDeviceState(mLastReceivedInfo.currentState));
             }
         }
     }
@@ -327,6 +376,8 @@
                 final int[] supportedStates = Arrays.copyOf(info.supportedStates,
                         info.supportedStates.length);
                 callbacks.get(i).notifySupportedStatesChanged(supportedStates);
+                callbacks.get(i).notifySupportedDeviceStatesChanged(
+                        createDeviceStateList(supportedStates));
             }
         }
         if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
@@ -337,6 +388,7 @@
         if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
             for (int i = 0; i < callbacks.size(); i++) {
                 callbacks.get(i).notifyStateChanged(info.currentState);
+                callbacks.get(i).notifyDeviceStateChanged(createDeviceState(info.currentState));
             }
         }
     }
@@ -369,6 +421,36 @@
         }
     }
 
+    /**
+     * Creates a {@link DeviceState} object from a device state identifier, with the
+     * {@link DeviceState} property that corresponds to what display is primary.
+     *
+     */
+    // TODO(b/325124054): Remove when system server refactor is completed
+    @NonNull
+    private DeviceState createDeviceState(int stateIdentifier) {
+        final Set<@DeviceState.DeviceStateProperties Integer> properties = new HashSet<>();
+        if (ArrayUtils.contains(sFoldedDeviceStates, stateIdentifier)) {
+            properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+        } else {
+            properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+        }
+        return new DeviceState(stateIdentifier, "" /* name */, properties);
+    }
+
+    /**
+     * Creates a list of {@link DeviceState} objects from an array of state identifiers.
+     */
+    // TODO(b/325124054): Remove when system server refactor is completed
+    @NonNull
+    private List<DeviceState> createDeviceStateList(int[] supportedStates) {
+        List<DeviceState> deviceStateList = new ArrayList<>();
+        for (int i = 0; i < supportedStates.length; i++) {
+            deviceStateList.add(createDeviceState(supportedStates[i]));
+        }
+        return deviceStateList;
+    }
+
     private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
         @Override
         public void onDeviceStateInfoChanged(DeviceStateInfo info) {
@@ -403,6 +485,11 @@
                     mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
         }
 
+        void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
+            mExecutor.execute(() ->
+                    mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
+        }
+
         void notifyBaseStateChanged(int newBaseState) {
             execute("notifyBaseStateChanged",
                     () -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
@@ -413,6 +500,11 @@
                     () -> mDeviceStateCallback.onStateChanged(newDeviceState));
         }
 
+        void notifyDeviceStateChanged(DeviceState newDeviceState) {
+            execute("notifyDeviceStateChanged",
+                    () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
+        }
+
         private void execute(String traceName, Runnable r) {
             mExecutor.execute(() -> {
                 if (DEBUG) {
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
new file mode 100644
index 0000000..73a9e34
--- /dev/null
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.hardware.devicestate.feature.flags"
+
+flag {
+    name: "device_state_property_api"
+    namespace: "windowing_sdk"
+    description: "Updated DeviceState hasProperty API"
+    bug: "293636629"
+    is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/service/chooser/CustomChoosers.java b/core/java/android/service/chooser/CustomChoosers.java
deleted file mode 100644
index 5b89432..0000000
--- a/core/java/android/service/chooser/CustomChoosers.java
+++ /dev/null
@@ -1,84 +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 android.service.chooser;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Static helper methods that privileged clients can use to initiate Share sessions with extra
- * customization options that aren't usually available in the stock "Resolver/Chooser" flows.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
-@SystemApi
-public class CustomChoosers {
-    /**
-     * Intent action to start a Share session with additional customization options. Clients should
-     * use the helper methods in this class to configure their customized share intents, and should
-     * avoid using this action to construct their own intents directly.
-     */
-    private static final String ACTION_SHOW_CUSTOMIZED_RESOLVER =
-            "android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER";
-
-    /**
-     * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
-     * targets in the customized share session.
-     *
-     * @hide
-     */
-    public static final String EXTRA_RESOLVE_INFOS = "android.service.chooser.extra.RESOLVE_INFOS";
-
-    /**
-     * Build an {@link Intent} to dispatch a "Chooser flow" that picks a target resolution for the
-     * specified {@code target} intent, styling the Chooser UI according to the specified
-     * customization parameters.
-     *
-     * @param target The ambiguous intent that should be resolved to a specific target selected
-     * via the Chooser flow.
-     * @param title An optional "headline" string to display at the top of the Chooser UI, or null
-     * to use the system default.
-     * @param resolutionList Explicit resolution info for targets that should be shown in the
-     * dispatched Share UI.
-     *
-     * @hide
-     */
-    @FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
-    @SystemApi
-    @NonNull
-    public static Intent createNfcResolverIntent(
-            @NonNull Intent target,
-            @Nullable CharSequence title,
-            @NonNull List<ResolveInfo> resolutionList) {
-        Intent resolverIntent = new Intent(ACTION_SHOW_CUSTOMIZED_RESOLVER);
-        resolverIntent.putExtra(Intent.EXTRA_INTENT, target);
-        resolverIntent.putExtra(Intent.EXTRA_TITLE, title);
-        resolverIntent.putParcelableArrayListExtra(
-                EXTRA_RESOLVE_INFOS, new ArrayList<>(resolutionList));
-        return resolverIntent;
-    }
-
-    private CustomChoosers() {}
-}
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index add575b..00236df 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -15,13 +15,6 @@
 }
 
 flag {
-  name: "support_nfc_resolver"
-  namespace: "systemui"
-  description: "This flag controls the new NFC 'resolver' activity"
-  bug: "268089816"
-}
-
-flag {
   name: "chooser_payload_toggling"
   namespace: "intentresolver"
   description: "This flag controls content toggling in Chooser"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f169ecd..15fb6cc 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -813,9 +813,9 @@
         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
 
-        // all default rules and user created rules updated to zenMode important interruptions
-        if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+        if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
                 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
+            // all default rules and user created rules updated to zenMode important interruptions
             Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
             rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         }
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.java b/core/java/android/service/voice/HotwordTrainingAudio.java
deleted file mode 100644
index 916fa36b..0000000
--- a/core/java/android/service/voice/HotwordTrainingAudio.java
+++ /dev/null
@@ -1,388 +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 android.service.voice;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.media.AudioFormat;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.voice.flags.Flags;
-
-import com.android.internal.util.DataClass;
-
-import java.util.Objects;
-
-/**
- * Represents audio supporting hotword model training.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-@DataClass(
-        genConstructor = false,
-        genBuilder = true,
-        genEqualsHashCode = true,
-        genHiddenConstDefs = true,
-        genParcelable = true,
-        genToString = true
-)
-@SystemApi
-public final class HotwordTrainingAudio implements Parcelable {
-    /** Represents unset value for the hotword offset. */
-    public static final int HOTWORD_OFFSET_UNSET = -1;
-
-    /**
-     * Buffer of hotword audio data for training models. The data format is expected to match
-     * {@link #getAudioFormat()}.
-     */
-    @NonNull
-    private final byte[] mHotwordAudio;
-
-    private String hotwordAudioToString() {
-        return "length=" + mHotwordAudio.length;
-    }
-
-    /**
-     * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
-     */
-    @NonNull
-    private final AudioFormat mAudioFormat;
-
-    /**
-     * App-defined identifier to distinguish hotword training audio instances.
-     * <p> Returns -1 if unset. */
-    @NonNull
-    private final int mAudioType;
-
-    private static int defaultAudioType() {
-        return -1;
-    }
-
-    /**
-     * App-defined offset in milliseconds relative to start of
-     * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
-     * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
-     */
-    private int mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
-
-    @DataClass.Suppress("setHotwordAudio")
-    abstract static class BaseBuilder {
-
-        /**
-         * Buffer of hotword audio data for training models. The data format is expected to match
-         * {@link #getAudioFormat()}.
-         */
-        @SuppressLint("UnflaggedApi")
-        public @NonNull HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte[] value) {
-            Objects.requireNonNull(value, "value should not be null");
-            final HotwordTrainingAudio.Builder builder = (HotwordTrainingAudio.Builder) this;
-            // If the code gen flag in build() is changed, we must update the flag e.g. 0x1 here.
-            builder.mBuilderFieldsSet |= 0x1;
-            builder.mHotwordAudio = value;
-            return builder;
-        }
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    /* package-private */ HotwordTrainingAudio(
-            @NonNull byte[] hotwordAudio,
-            @NonNull AudioFormat audioFormat,
-            @NonNull int audioType,
-            int hotwordOffsetMillis) {
-        this.mHotwordAudio = hotwordAudio;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mHotwordAudio);
-        this.mAudioFormat = audioFormat;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioFormat);
-        this.mAudioType = audioType;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioType);
-        this.mHotwordOffsetMillis = hotwordOffsetMillis;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * Buffer of hotword audio data for training models. The data format is expected to match
-     * {@link #getAudioFormat()}.
-     */
-    @DataClass.Generated.Member
-    public @NonNull byte[] getHotwordAudio() {
-        return mHotwordAudio;
-    }
-
-    /**
-     * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
-     */
-    @DataClass.Generated.Member
-    public @NonNull AudioFormat getAudioFormat() {
-        return mAudioFormat;
-    }
-
-    /**
-     * App-defined identifier to distinguish hotword training audio instances.
-     * <p> Returns -1 if unset.
-     */
-    @DataClass.Generated.Member
-    public @NonNull int getAudioType() {
-        return mAudioType;
-    }
-
-    /**
-     * App-defined offset in milliseconds relative to start of
-     * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
-     * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
-     */
-    @DataClass.Generated.Member
-    public int getHotwordOffsetMillis() {
-        return mHotwordOffsetMillis;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "HotwordTrainingAudio { " +
-                "hotwordAudio = " + hotwordAudioToString() + ", " +
-                "audioFormat = " + mAudioFormat + ", " +
-                "audioType = " + mAudioType + ", " +
-                "hotwordOffsetMillis = " + mHotwordOffsetMillis +
-        " }";
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(HotwordTrainingAudio other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        HotwordTrainingAudio that = (HotwordTrainingAudio) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && java.util.Arrays.equals(mHotwordAudio, that.mHotwordAudio)
-                && Objects.equals(mAudioFormat, that.mAudioFormat)
-                && mAudioType == that.mAudioType
-                && mHotwordOffsetMillis == that.mHotwordOffsetMillis;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + java.util.Arrays.hashCode(mHotwordAudio);
-        _hash = 31 * _hash + Objects.hashCode(mAudioFormat);
-        _hash = 31 * _hash + mAudioType;
-        _hash = 31 * _hash + mHotwordOffsetMillis;
-        return _hash;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeByteArray(mHotwordAudio);
-        dest.writeTypedObject(mAudioFormat, flags);
-        dest.writeInt(mAudioType);
-        dest.writeInt(mHotwordOffsetMillis);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ HotwordTrainingAudio(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        byte[] hotwordAudio = in.createByteArray();
-        AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
-        int audioType = in.readInt();
-        int hotwordOffsetMillis = in.readInt();
-
-        this.mHotwordAudio = hotwordAudio;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mHotwordAudio);
-        this.mAudioFormat = audioFormat;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioFormat);
-        this.mAudioType = audioType;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioType);
-        this.mHotwordOffsetMillis = hotwordOffsetMillis;
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<HotwordTrainingAudio> CREATOR
-            = new Parcelable.Creator<HotwordTrainingAudio>() {
-        @Override
-        public HotwordTrainingAudio[] newArray(int size) {
-            return new HotwordTrainingAudio[size];
-        }
-
-        @Override
-        public HotwordTrainingAudio createFromParcel(@NonNull Parcel in) {
-            return new HotwordTrainingAudio(in);
-        }
-    };
-
-    /**
-     * A builder for {@link HotwordTrainingAudio}
-     */
-    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-    @SuppressWarnings("WeakerAccess")
-    @DataClass.Generated.Member
-    public static final class Builder extends BaseBuilder {
-
-        private @NonNull byte[] mHotwordAudio;
-        private @NonNull AudioFormat mAudioFormat;
-        private @NonNull int mAudioType;
-        private int mHotwordOffsetMillis;
-
-        private long mBuilderFieldsSet = 0L;
-
-        /**
-         * Creates a new Builder.
-         *
-         * @param hotwordAudio
-         *   Buffer of hotword audio data for training models. The data format is expected to match
-         *   {@link #getAudioFormat()}.
-         * @param audioFormat
-         *   The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
-         */
-        public Builder(
-                @NonNull byte[] hotwordAudio,
-                @NonNull AudioFormat audioFormat) {
-            mHotwordAudio = hotwordAudio;
-            com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mHotwordAudio);
-            mAudioFormat = audioFormat;
-            com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mAudioFormat);
-        }
-
-        /**
-         * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mAudioFormat = value;
-            return this;
-        }
-
-        /**
-         * App-defined identifier to distinguish hotword training audio instances.
-         * <p> Returns -1 if unset.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setAudioType(@NonNull int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x4;
-            mAudioType = value;
-            return this;
-        }
-
-        /**
-         * App-defined offset in milliseconds relative to start of
-         * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
-         * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setHotwordOffsetMillis(int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x8;
-            mHotwordOffsetMillis = value;
-            return this;
-        }
-
-        /** Builds the instance. This builder should not be touched after calling this! */
-        public @NonNull HotwordTrainingAudio build() {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x10; // Mark builder used
-
-            if ((mBuilderFieldsSet & 0x4) == 0) {
-                mAudioType = defaultAudioType();
-            }
-            if ((mBuilderFieldsSet & 0x8) == 0) {
-                mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
-            }
-            HotwordTrainingAudio o = new HotwordTrainingAudio(
-                    mHotwordAudio,
-                    mAudioFormat,
-                    mAudioType,
-                    mHotwordOffsetMillis);
-            return o;
-        }
-
-        private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x10) != 0) {
-                throw new IllegalStateException(
-                        "This Builder should not be reused. Use a new Builder instance instead");
-            }
-        }
-    }
-
-    @DataClass.Generated(
-            time = 1697827049629L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java",
-            inputSignatures = "public static final  int HOTWORD_OFFSET_UNSET\nprivate final @android.annotation.NonNull byte[] mHotwordAudio\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull int mAudioType\nprivate  int mHotwordOffsetMillis\nprivate  java.lang.String hotwordAudioToString()\nprivate static  int defaultAudioType()\nclass HotwordTrainingAudio extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(byte[])\nclass BaseBuilder extends java.lang.Object implements []")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/service/voice/HotwordTrainingData.aidl b/core/java/android/service/voice/HotwordTrainingData.aidl
deleted file mode 100644
index 03cc841..0000000
--- a/core/java/android/service/voice/HotwordTrainingData.aidl
+++ /dev/null
@@ -1,19 +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 android.service.voice;
-
-parcelable HotwordTrainingData;
diff --git a/core/java/android/service/voice/HotwordTrainingData.java b/core/java/android/service/voice/HotwordTrainingData.java
deleted file mode 100644
index aa6dab3..0000000
--- a/core/java/android/service/voice/HotwordTrainingData.java
+++ /dev/null
@@ -1,305 +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 android.service.voice;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.voice.flags.Flags;
-import android.text.TextUtils;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Contains training data related to hotword detection service.
- *
- * <p>The constructed object's size must be within
- * {@link HotwordTrainingData#getMaxTrainingDataBytes()} or an
- * {@link IllegalArgumentException} will be thrown on construction. Size of the object is calculated
- * by converting object to a {@link Parcel} and using the {@link Parcel#dataSize()}.
- *
- * @hide
- */
-@DataClass(
-        genConstructor = false,
-        genBuilder = true,
-        genEqualsHashCode = true,
-        genHiddenConstDefs = true,
-        genParcelable = true,
-        genToString = true)
-@SystemApi
-@FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-public final class HotwordTrainingData implements Parcelable {
-    /** Max size for hotword training data in bytes. */
-    public static int getMaxTrainingDataBytes() {
-        return 1024 * 1024; // 1 MB;
-    }
-
-    /** The list containing hotword audio that is useful for training. */
-    @NonNull
-    @DataClass.PluralOf("trainingAudio")
-    private final List<HotwordTrainingAudio> mTrainingAudioList;
-
-    private static List<HotwordTrainingAudio> defaultTrainingAudioList() {
-        return Collections.emptyList();
-    }
-
-    /** App-defined stage when hotword model timed-out while running.
-     * <p> Returns -1 if unset. */
-    private final int mTimeoutStage;
-
-    private static int defaultTimeoutStage() {
-        return -1;
-    }
-
-    private void onConstructed() {
-        // Verify size of object is within limit.
-        Parcel parcel = Parcel.obtain();
-        parcel.writeValue(this);
-        int dataSizeBytes = parcel.dataSize();
-        parcel.recycle();
-        Preconditions.checkArgument(
-                dataSizeBytes < getMaxTrainingDataBytes(),
-                TextUtils.formatSimple(
-                        "Hotword training data of size %s exceeds size limit of %s bytes!",
-                        dataSizeBytes, getMaxTrainingDataBytes()));
-    }
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingData.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @DataClass.Generated.Member
-    /* package-private */ HotwordTrainingData(
-            @NonNull List<HotwordTrainingAudio> trainingAudioList,
-            int timeoutStage) {
-        this.mTrainingAudioList = trainingAudioList;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mTrainingAudioList);
-        this.mTimeoutStage = timeoutStage;
-
-        onConstructed();
-    }
-
-    /**
-     * The list containing hotword audio that is useful for training.
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<HotwordTrainingAudio> getTrainingAudioList() {
-        return mTrainingAudioList;
-    }
-
-    /**
-     * App-defined stage when hotword model timed-out while running.
-     * <p> Returns -1 if unset.
-     */
-    @DataClass.Generated.Member
-    public int getTimeoutStage() {
-        return mTimeoutStage;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "HotwordTrainingData { " +
-                "trainingAudioList = " + mTrainingAudioList + ", " +
-                "timeoutStage = " + mTimeoutStage +
-        " }";
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public boolean equals(@android.annotation.Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(HotwordTrainingData other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        HotwordTrainingData that = (HotwordTrainingData) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && java.util.Objects.equals(mTrainingAudioList, that.mTrainingAudioList)
-                && mTimeoutStage == that.mTimeoutStage;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudioList);
-        _hash = 31 * _hash + mTimeoutStage;
-        return _hash;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeParcelableList(mTrainingAudioList, flags);
-        dest.writeInt(mTimeoutStage);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ HotwordTrainingData(@NonNull Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        List<HotwordTrainingAudio> trainingAudioList = new ArrayList<>();
-        in.readParcelableList(trainingAudioList, HotwordTrainingAudio.class.getClassLoader());
-        int timeoutStage = in.readInt();
-
-        this.mTrainingAudioList = trainingAudioList;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mTrainingAudioList);
-        this.mTimeoutStage = timeoutStage;
-
-        onConstructed();
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<HotwordTrainingData> CREATOR
-            = new Parcelable.Creator<HotwordTrainingData>() {
-        @Override
-        public HotwordTrainingData[] newArray(int size) {
-            return new HotwordTrainingData[size];
-        }
-
-        @Override
-        public HotwordTrainingData createFromParcel(@NonNull Parcel in) {
-            return new HotwordTrainingData(in);
-        }
-    };
-
-    /**
-     * A builder for {@link HotwordTrainingData}
-     */
-    @FlaggedApi(Flags.FLAG_ALLOW_TRAINING_DATA_EGRESS_FROM_HDS)
-    @SuppressWarnings("WeakerAccess")
-    @DataClass.Generated.Member
-    public static final class Builder {
-
-        private @NonNull List<HotwordTrainingAudio> mTrainingAudioList;
-        private int mTimeoutStage;
-
-        private long mBuilderFieldsSet = 0L;
-
-        public Builder() {
-        }
-
-        /**
-         * The list containing hotword audio that is useful for training.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setTrainingAudioList(@NonNull List<HotwordTrainingAudio> value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x1;
-            mTrainingAudioList = value;
-            return this;
-        }
-
-        /** @see #setTrainingAudioList */
-        @DataClass.Generated.Member
-        public @NonNull Builder addTrainingAudio(@NonNull HotwordTrainingAudio value) {
-            if (mTrainingAudioList == null) setTrainingAudioList(new ArrayList<>());
-            mTrainingAudioList.add(value);
-            return this;
-        }
-
-        /**
-         * App-defined stage when hotword model timed-out while running.
-         * <p> Returns -1 if unset.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setTimeoutStage(int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mTimeoutStage = value;
-            return this;
-        }
-
-        /** Builds the instance. This builder should not be touched after calling this! */
-        public @NonNull HotwordTrainingData build() {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
-
-            if ((mBuilderFieldsSet & 0x1) == 0) {
-                mTrainingAudioList = defaultTrainingAudioList();
-            }
-            if ((mBuilderFieldsSet & 0x2) == 0) {
-                mTimeoutStage = defaultTimeoutStage();
-            }
-            HotwordTrainingData o = new HotwordTrainingData(
-                    mTrainingAudioList,
-                    mTimeoutStage);
-            return o;
-        }
-
-        private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
-                throw new IllegalStateException(
-                        "This Builder should not be reused. Use a new Builder instance instead");
-            }
-        }
-    }
-
-    @DataClass.Generated(
-            time = 1697826948280L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingData.java",
-            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudioList\nprivate final  int mTimeoutStage\npublic static  int getMaxTrainingDataBytes()\nprivate static  java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudioList()\nprivate static  int defaultTimeoutStage()\nprivate  void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 95d1974..2b6684e 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -237,10 +237,13 @@
             // Easy case: If the line instance only contains single directionality run, no need
             // to reorder visually.
             if (bidi.getRunCount() == 1) {
-                if ((bidi.getParaLevel() & 0x01) == 1) {
+                if (bidi.getRunLevel(0) == 1) {
                     return Layout.DIRS_ALL_RIGHT_TO_LEFT;
-                } else {
+                } else if (bidi.getRunLevel(0) == 0) {
                     return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+                } else {
+                    return new Directions(new int[] {
+                            0, bidi.getRunLevel(0) << Layout.RUN_LEVEL_SHIFT | (end - start)});
                 }
             }
 
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java
index 5ec41591..5406cf5 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -192,7 +192,7 @@
      * {@link WindowManager#registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken,
      * SurfaceControl, Looper, SurfaceControlInputReceiver)}
      *
-     * @return The SurfaceControlViewHost link token.
+     * @return The {@link InputTransferToken} for the {@link AttachedSurfaceControl}
      * @throws IllegalStateException if the {@link AttachedSurfaceControl} was created with no
      * registered input
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3c0ac06..eff35c0c0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -224,6 +224,8 @@
             @DataSpace.NamedDataSpace int dataSpace);
     private static native void nativeSetExtendedRangeBrightness(long transactionObj,
             long nativeObject, float currentBufferRatio, float desiredRatio);
+    private static native void nativeSetDesiredHdrHeadroom(long transactionObj,
+            long nativeObject, float desiredRatio);
 
     private static native void nativeSetCachingHint(long transactionObj,
             long nativeObject, int cachingHint);
@@ -4148,6 +4150,50 @@
         }
 
         /**
+         * Sets the desired hdr headroom for the layer.
+         *
+         * <p>Prefer using this API over {@link #setExtendedRangeBrightness} for formats that
+         *. conform to HDR video standards like HLG or HDR10 which do not communicate a HDR/SDR
+         * ratio as part of generating the buffer.
+         *
+         * @param sc The layer whose desired hdr headroom is being specified
+         *
+         * @param desiredRatio The desired hdr/sdr ratio. This can be used to communicate the max
+         *                     desired brightness range. This is similar to the "max luminance"
+         *                     value in other HDR metadata formats, but represented as a ratio of
+         *                     the target SDR whitepoint to the max display brightness. The system
+         *                     may not be able to, or may choose not to, deliver the
+         *                     requested range.
+         *
+         *                     <p>Default value is 0.0f and indicates that the system will choose
+         *                     the best headroom for this surface control's content. Typically,
+         *                     this means that HLG/PQ encoded content will be displayed with some
+         *                     HDR headroom greater than 1.0.
+         *
+         *                     <p>When called after {@link #setExtendedRangeBrightness}, the
+         *                     desiredHeadroom will override the desiredRatio provided by
+         *                     {@link #setExtendedRangeBrightness}. Conversely, when called
+         *                     before {@link #setExtendedRangeBrightness}, the desiredRatio provided
+         *                     by {@link #setExtendedRangeBrightness} will override the
+         *                     desiredHeadroom.
+         *
+         *                     <p>Must be finite && >= 1.0f or 0.0f.
+         * @return this
+         * @see #setExtendedRangeBrightness
+         **/
+        @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+        public @NonNull Transaction setDesiredHdrHeadroom(@NonNull SurfaceControl sc,
+                @FloatRange(from = 0.0f) float desiredRatio) {
+            checkPreconditions(sc);
+            if (!Float.isFinite(desiredRatio) || (desiredRatio != 0 && desiredRatio < 1.0f)) {
+                throw new IllegalArgumentException(
+                        "desiredRatio must be finite && >= 1.0f or 0; got " + desiredRatio);
+            }
+            nativeSetDesiredHdrHeadroom(mNativeObject, sc.mNativeObject, desiredRatio);
+            return this;
+        }
+
+        /**
          * Sets the caching hint for the layer. By default, the caching hint is
          * {@link CACHING_ENABLED}.
          *
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 06a923a..6f757df 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -293,6 +293,7 @@
         /**
          * Gets an {@link InputTransferToken} which can be used to request focus on the embedded
          * surface or to transfer touch gesture to the embedded surface.
+         *
          * @return the InputTransferToken associated with {@link SurfacePackage} or {@code null} if
          * the embedded hasn't set up its view or doesn't have input.
          * @see WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken)
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9caf7a6..d494e28 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -21,6 +21,8 @@
 import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
 
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -187,6 +189,7 @@
 
     final Rect mScreenRect = new Rect();
     private final SurfaceSession mSurfaceSession = new SurfaceSession();
+    private final boolean mLimitedHdrEnabled = Flags.limitedHdr();
 
     SurfaceControl mSurfaceControl;
     SurfaceControl mBackgroundControl;
@@ -197,6 +200,9 @@
     @SurfaceLifecycleStrategy
     private int mSurfaceLifecycleStrategy = SURFACE_LIFECYCLE_DEFAULT;
 
+    private float mRequestedHdrHeadroom = 0.f;
+    private float mHdrHeadroom = 0.f;
+
     /**
      * We use this lock to protect access to mSurfaceControl. Both are accessed on the UI
      * thread and the render thread via RenderNode.PositionUpdateListener#positionLost.
@@ -821,6 +827,45 @@
         updateSurface();
     }
 
+
+    /**
+     * Sets the desired amount of HDR headroom to be used when HDR content is presented on this
+     * SurfaceView.
+     *
+     * <p>By default the system will choose an amount of HDR headroom that is appropriate
+     * for the underlying device capabilities & bit-depth of the panel. However, for some types
+     * of content this can end up being more headroom than necessary or desired. An example
+     * would be a messaging app or gallery thumbnail view where some amount of HDR pop is desired
+     * without overly influencing the perceived brightness of the majority SDR content. This can
+     * also be used to animate in/out of an HDR range for smoother transitions.</p>
+     *
+     * <p>Note: The actual amount of HDR headroom that will be given is subject to a variety
+     * of factors such as ambient conditions, display capabilities, or bit-depth limitations.
+     * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the
+     * current value.</p>
+     *
+     * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR)
+     *                        and <= 10,000.0. Passing 0.0 will reset to the default, automatically
+     *                        chosen value.
+     * @see Display#getHdrSdrRatio()
+     */
+    @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+    public void setDesiredHdrHeadroom(
+            @FloatRange(from = 0.0f, to = 10000.0) float desiredHeadroom) {
+        if (!Float.isFinite(desiredHeadroom)) {
+            throw new IllegalArgumentException("desiredHeadroom must be finite: "
+                    + desiredHeadroom);
+        }
+        if (desiredHeadroom != 0 && (desiredHeadroom < 1.0f || desiredHeadroom > 10000.0f)) {
+            throw new IllegalArgumentException(
+                    "desiredHeadroom must be 0.0 or in the range [1.0, 10000.0f], received: "
+                            + desiredHeadroom);
+        }
+        mRequestedHdrHeadroom = desiredHeadroom;
+        updateSurface();
+        invalidate();
+    }
+
     private void updateOpaqueFlag() {
         if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
             mSurfaceFlags |= SurfaceControl.OPAQUE;
@@ -941,6 +986,10 @@
 
             updateBackgroundVisibility(surfaceUpdateTransaction);
             updateBackgroundColor(surfaceUpdateTransaction);
+            if (mLimitedHdrEnabled) {
+                surfaceUpdateTransaction.setDesiredHdrHeadroom(
+                        mBlastSurfaceControl, mHdrHeadroom);
+            }
             if (isAboveParent()) {
                 float alpha = getAlpha();
                 surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
@@ -1085,11 +1134,12 @@
         final boolean relativeZChanged = mSubLayer != mRequestedSubLayer;
         final boolean surfaceLifecycleStrategyChanged =
                 mSurfaceLifecycleStrategy != mRequestedSurfaceLifecycleStrategy;
+        final boolean hdrHeadroomChanged = mHdrHeadroom != mRequestedHdrHeadroom;
 
         if (creating || formatChanged || sizeChanged || visibleChanged
                 || alphaChanged || windowVisibleChanged || positionChanged
                 || layoutSizeChanged || hintChanged || relativeZChanged || !mAttachedToWindow
-                || surfaceLifecycleStrategyChanged) {
+                || surfaceLifecycleStrategyChanged || hdrHeadroomChanged) {
 
             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                     + "Changes: creating=" + creating
@@ -1117,6 +1167,7 @@
 
                 final int previousSurfaceLifecycleStrategy = mSurfaceLifecycleStrategy;
                 mSurfaceLifecycleStrategy = mRequestedSurfaceLifecycleStrategy;
+                mHdrHeadroom = mRequestedHdrHeadroom;
 
                 mScreenRect.left = mWindowSpaceLeft;
                 mScreenRect.top = mWindowSpaceTop;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0e17626..64846d0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -6184,7 +6184,7 @@
      * receive batched input event. For those events that are batched, the invocation will happen
      * once per {@link Choreographer} frame, and other input events will be delivered immediately.
      * This is different from
-     * { #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
      * Looper, SurfaceControlInputReceiver)} in that the input events are received batched. The
      * caller must invoke {@link #unregisterSurfaceControlInputReceiver(SurfaceControl)} to clean up
      * the resources when no longer needing to use the {@link SurfaceControlInputReceiver}
@@ -6253,10 +6253,9 @@
      * <p>
      * Must be called on the same {@link Looper} thread to which was passed to the
      * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
-     * Choreographer,
-     * SurfaceControlInputReceiver)} or
-     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, Looper,
-     * SurfaceControlInputReceiver)}
+     * Choreographer, SurfaceControlInputReceiver)} or
+     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+     * Looper, SurfaceControlInputReceiver)}
      *
      * @param surfaceControl The SurfaceControl to remove and unregister the input channel for.
      */
@@ -6267,12 +6266,12 @@
     }
 
     /**
-     * Returns the input client token for the {@link SurfaceControl}. This will only return non null
-     * if the SurfaceControl was registered for input via
-     * { #registerBatchedSurfaceControlInputReceiver(int, IBinder, SurfaceControl, Choreographer,
-     * SurfaceControlInputReceiver)} or
-     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl, Looper,
-     * SurfaceControlInputReceiver)}.
+     * Returns the input client token for the {@link SurfaceControl}. This will only return non
+     * null if the SurfaceControl was registered for input via
+     * {@link #registerBatchedSurfaceControlInputReceiver(int, InputTransferToken, SurfaceControl,
+     * Choreographer, SurfaceControlInputReceiver)} or
+     * {@link #registerUnbatchedSurfaceControlInputReceiver(int, InputTransferToken,
+     * SurfaceControl, Looper, SurfaceControlInputReceiver)}.
      * <p>
      * This is helpful for testing to ensure the test waits for the layer to be registered with
      * SurfaceFlinger and Input before proceeding with the test.
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 584219a..c6d0454 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -898,7 +898,7 @@
         return inputTransferToken;
     }
 
-    void unregisterSurfaceControlInputReceiver(SurfaceControl surfaceControl) {
+    void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
         SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
         synchronized (mSurfaceControlInputReceivers) {
             surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.removeReturnOld(
@@ -920,7 +920,7 @@
         surfaceControlInputReceiverInfo.mInputEventReceiver.dispose();
     }
 
-    IBinder getSurfaceControlInputClientToken(SurfaceControl surfaceControl) {
+    IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
         SurfaceControlInputReceiverInfo surfaceControlInputReceiverInfo;
         synchronized (mSurfaceControlInputReceivers) {
             surfaceControlInputReceiverInfo = mSurfaceControlInputReceivers.get(
@@ -934,8 +934,8 @@
         return surfaceControlInputReceiverInfo.mClientToken;
     }
 
-    boolean transferTouchGesture(InputTransferToken transferFromToken,
-            InputTransferToken transferToToken) {
+    boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+            @NonNull InputTransferToken transferToToken) {
         try {
             return getWindowManagerService().transferTouchGesture(transferFromToken,
                     transferToToken);
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 8972228..df4fed6 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -539,6 +539,10 @@
             @NonNull InputTransferToken hostInputTransferToken,
             @NonNull SurfaceControl surfaceControl, @NonNull Choreographer choreographer,
             @NonNull SurfaceControlInputReceiver receiver) {
+        Objects.requireNonNull(hostInputTransferToken);
+        Objects.requireNonNull(surfaceControl);
+        Objects.requireNonNull(choreographer);
+        Objects.requireNonNull(receiver);
         return mGlobal.registerBatchedSurfaceControlInputReceiver(displayId, hostInputTransferToken,
                 surfaceControl, choreographer, receiver);
     }
@@ -549,18 +553,24 @@
             @NonNull InputTransferToken hostInputTransferToken,
             @NonNull SurfaceControl surfaceControl, @NonNull Looper looper,
             @NonNull SurfaceControlInputReceiver receiver) {
+        Objects.requireNonNull(hostInputTransferToken);
+        Objects.requireNonNull(surfaceControl);
+        Objects.requireNonNull(looper);
+        Objects.requireNonNull(receiver);
         return mGlobal.registerUnbatchedSurfaceControlInputReceiver(displayId,
                 hostInputTransferToken, surfaceControl, looper, receiver);
     }
 
     @Override
     public void unregisterSurfaceControlInputReceiver(@NonNull SurfaceControl surfaceControl) {
+        Objects.requireNonNull(surfaceControl);
         mGlobal.unregisterSurfaceControlInputReceiver(surfaceControl);
     }
 
     @Override
     @Nullable
     public IBinder getSurfaceControlInputClientToken(@NonNull SurfaceControl surfaceControl) {
+        Objects.requireNonNull(surfaceControl);
         return mGlobal.getSurfaceControlInputClientToken(surfaceControl);
     }
 
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 57a3b76..8a407c3 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -185,7 +185,7 @@
     @FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_READ_ONLY)
     public static long getExpectedPresentationTimeNanos() {
         if (!sExpectedPresentationTimeFlagValue) {
-            return SystemClock.uptimeMillis();
+            return SystemClock.uptimeMillis() * TimeUtils.NANOS_PER_MS;
         }
 
         AnimationState state = sAnimationState.get();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5bf1b5b..13dc4ef 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -112,6 +112,7 @@
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.IRemoteViewsFactory;
+import com.android.internal.widget.remotecompose.core.operations.Theme;
 import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
 import com.android.internal.widget.remotecompose.player.RemoteComposePlayer;
 
@@ -3902,7 +3903,6 @@
                 throws ActionException {
             if (drawDataParcel() && mInstructions != null
                     && root instanceof RemoteComposePlayer player) {
-                player.setTag(mInstructions);
                 final List<byte[]> bytes = mInstructions.mInstructions;
                 if (bytes.isEmpty()) {
                     return;
@@ -6082,20 +6082,24 @@
         if (applyThemeResId != 0) {
             inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
         }
+        View v;
         // If the RemoteViews contains draw instructions, just use it instead.
         if (rv.hasDrawInstructions()) {
-            return new RemoteComposePlayer(inflationContext);
-        }
-        LayoutInflater inflater = LayoutInflater.from(context);
+            final RemoteComposePlayer player = new RemoteComposePlayer(inflationContext);
+            player.setDebug(Build.IS_USERDEBUG || Build.IS_ENG ? 1 : 0);
+            v = player;
+        } else {
+            LayoutInflater inflater = LayoutInflater.from(context);
 
-        // Clone inflater so we load resources from correct context and
-        // we don't add a filter to the static version returned by getSystemService.
-        inflater = inflater.cloneInContext(inflationContext);
-        inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
-        if (mLayoutInflaterFactory2 != null) {
-            inflater.setFactory2(mLayoutInflaterFactory2);
+            // Clone inflater so we load resources from correct context and
+            // we don't add a filter to the static version returned by getSystemService.
+            inflater = inflater.cloneInContext(inflationContext);
+            inflater.setFilter(shouldUseStaticFilter() ? INFLATER_FILTER : this);
+            if (mLayoutInflaterFactory2 != null) {
+                inflater.setFactory2(mLayoutInflaterFactory2);
+            }
+            v = inflater.inflate(rv.getLayoutId(), parent, false);
         }
-        View v = inflater.inflate(rv.getLayoutId(), parent, false);
         if (mViewId != View.NO_ID) {
             v.setId(mViewId);
             v.setTagInternal(R.id.remote_views_override_id, mViewId);
@@ -6441,6 +6445,10 @@
         if (params.handler == null) {
             params.handler = DEFAULT_INTERACTION_HANDLER;
         }
+        if (v instanceof RemoteComposePlayer player) {
+            player.setTheme(v.getResources().getConfiguration().isNightModeActive()
+                    ? Theme.DARK : Theme.LIGHT);
+        }
         if (mActions != null) {
             final int count = mActions.size();
             for (int i = 0; i < count; i++) {
@@ -7565,6 +7573,8 @@
     @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
     public static final class DrawInstructions {
 
+        private static final long VERSION = 1L;
+
         @NonNull
         final List<byte[]> mInstructions;
 
@@ -7599,6 +7609,7 @@
             }
             return new DrawInstructions(instructions);
         }
+
         private static void writeToParcel(@Nullable final DrawInstructions drawInstructions,
                 @NonNull final Parcel dest, final int flags) {
             if (drawInstructions == null) {
@@ -7614,6 +7625,14 @@
         }
 
         /**
+         * Version number of {@link DrawInstructions} currently supported.
+         */
+        @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
+        public static long getSupportedVersion() {
+            return VERSION;
+        }
+
+        /**
          * Builder class for {@link DrawInstructions} objects.
          */
         @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9847cb1..1721462 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5513,6 +5513,7 @@
      *
      * @attr ref android.R.styleable#TextView_fontVariationSettings
      */
+    @android.view.RemotableViewMethod
     public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
         final String existingSettings = mTextPaint.getFontVariationSettings();
         if (fontVariationSettings == existingSettings
diff --git a/core/java/com/android/internal/app/NfcResolverActivity.java b/core/java/com/android/internal/app/NfcResolverActivity.java
index 402192a..78427fe 100644
--- a/core/java/com/android/internal/app/NfcResolverActivity.java
+++ b/core/java/com/android/internal/app/NfcResolverActivity.java
@@ -16,25 +16,25 @@
 
 package com.android.internal.app;
 
-import static android.service.chooser.CustomChoosers.EXTRA_RESOLVE_INFOS;
-import static android.service.chooser.Flags.supportNfcResolver;
+import static android.nfc.Flags.enableNfcMainline;
 
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
+import android.nfc.NfcAdapter;
 import android.os.Bundle;
 
 import java.util.ArrayList;
 
 /**
  * Caller-customizable variant of {@link ResolverActivity} to support the
- * {@link CustomChoosers#showNfcResolver()} API.
+ * NFC resolver intent.
  */
 public class NfcResolverActivity extends ResolverActivity {
 
     @Override
     @SuppressWarnings("MissingSuperCall")  // Called indirectly via `super_onCreate()`.
     protected void onCreate(Bundle savedInstanceState) {
-        if (!supportNfcResolver()) {
+        if (!enableNfcMainline()) {
             super_onCreate(savedInstanceState);
             finish();
             return;
@@ -43,7 +43,8 @@
         Intent intent = getIntent();
         Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
         ArrayList<ResolveInfo> rList =
-            intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS, ResolveInfo.class);
+                intent.getParcelableArrayListExtra(
+                NfcAdapter.EXTRA_RESOLVE_INFOS, ResolveInfo.class);
         CharSequence title = intent.getExtras().getCharSequence(
                 Intent.EXTRA_TITLE,
                 getResources().getText(com.android.internal.R.string.chooseActivity));
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 98f409a..6fec527a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -716,6 +716,13 @@
     transaction->setExtendedRangeBrightness(ctrl, currentBufferRatio, desiredRatio);
 }
 
+static void nativeSetDesiredHdrHeadroom(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                        jlong nativeObject, float desiredRatio) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    transaction->setDesiredHdrHeadroom(ctrl, desiredRatio);
+}
+
 static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj,
                                  jlong nativeObject, jint cachingHint) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2340,7 +2347,9 @@
             (void*)nativeSetDataSpace },
     {"nativeSetExtendedRangeBrightness", "(JJFF)V",
             (void*)nativeSetExtendedRangeBrightness },
-            {"nativeSetCachingHint", "(JJI)V",
+    {"nativeSetDesiredHdrHeadroom", "(JJF)V",
+            (void*)nativeSetDesiredHdrHeadroom },
+    {"nativeSetCachingHint", "(JJI)V",
             (void*)nativeSetCachingHint },
     {"nativeAddWindowInfosReportedListener", "(JLjava/lang/Runnable;)V",
             (void*)nativeAddWindowInfosReportedListener },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 71f06f1..33af172 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7696,7 +7696,7 @@
 
     <!-- @SystemApi Allows the holder to launch an Intent Resolver flow with custom presentation
          and/or targets.
-         @FlaggedApi("android.service.chooser.support_nfc_resolver")
+         @FlaggedApi("android.nfc.enable_nfc_mainline")
          @hide -->
     <permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER"
                 android:protectionLevel="signature|privileged" />
@@ -8196,8 +8196,8 @@
                 android:multiprocess="true"
                 android:permission="android.permission.SHOW_CUSTOMIZED_RESOLVER"
                 android:exported="true">
-            <intent-filter>
-                <action android:name="android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER" />
+            <intent-filter android:priority="100" >
+                <action android:name="android.nfc.action.SHOW_NFC_RESOLVER" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 3d5494d..15c9047 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -16,8 +16,6 @@
 
 package android.widget;
 
-import static android.appwidget.flags.Flags.drawDataParcel;
-
 import static com.android.internal.R.id.pending_intent_tag;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -65,7 +63,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
@@ -417,25 +414,6 @@
         assertNotNull(view.findViewById(R.id.light_background_text));
     }
 
-    @Test
-    public void remoteCanvasCanAccessDrawInstructions() {
-        if (!drawDataParcel()) {
-            return;
-        }
-        final byte[] bytes = new byte[] {'h', 'e', 'l', 'l', 'o'};
-        final RemoteViews.DrawInstructions drawInstructions =
-                new RemoteViews.DrawInstructions.Builder(Collections.singletonList(bytes)).build();
-        final RemoteViews rv = new RemoteViews(drawInstructions);
-        final PendingIntent pi = PendingIntent.getActivity(mContext, 0,
-                new Intent(Intent.ACTION_VIEW), PendingIntent.FLAG_IMMUTABLE);
-        final Intent i = new Intent().putExtra("TEST", "Success");
-        final int viewId = 1;
-        rv.setPendingIntentTemplate(viewId, pi);
-        rv.setOnClickFillInIntent(viewId, i);
-        final View view = rv.apply(mContext, mContainer);
-        assertEquals(drawInstructions, view.getTag());
-    }
-
     private RemoteViews createViewChained(int depth, String... texts) {
         RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
 
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
index 396d403..fb5e512 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
@@ -16,8 +16,8 @@
 
 package android.hardware.devicestate;
 
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNull;
@@ -39,18 +39,18 @@
 public final class DeviceStateTest {
     @Test
     public void testConstruct() {
-        final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
+        final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
                 "TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
-        assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
+        assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE_IDENTIFIER);
         assertEquals(state.getName(), "TEST_CLOSED");
         assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
     }
 
     @Test
     public void testConstruct_nullName() {
-        final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
+        final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
                 null /* name */, 0/* flags */);
-        assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
+        assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE_IDENTIFIER);
         assertNull(state.getName());
         assertEquals(state.getFlags(), 0);
     }
@@ -58,7 +58,8 @@
     @Test
     public void testConstruct_tooLargeIdentifier() {
         assertThrows(IllegalArgumentException.class, () -> {
-            final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
+            final DeviceState state = new DeviceState(
+                    MAXIMUM_DEVICE_STATE_IDENTIFIER + 1 /* identifier */,
                     null /* name */, 0 /* flags */);
         });
     }
@@ -66,7 +67,8 @@
     @Test
     public void testConstruct_tooSmallIdentifier() {
         assertThrows(IllegalArgumentException.class, () -> {
-            final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
+            final DeviceState state = new DeviceState(
+                    MINIMUM_DEVICE_STATE_IDENTIFIER - 1 /* identifier */,
                     null /* name */, 0 /* flags */);
         });
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
new file mode 100644
index 0000000..4c34971
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.wm.shell.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.view.WindowManager
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
+import com.android.wm.shell.util.KtProtoLog
+import java.util.Arrays
+
+/**
+ * Helper for multi-instance related checks.
+ */
+class MultiInstanceHelper @JvmOverloads constructor(
+    private val context: Context,
+    private val packageManager: PackageManager,
+    private val staticAppsSupportingMultiInstance: Array<String> = context.resources
+            .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) {
+
+    /**
+     * Returns whether a specific component desires to be launched in multiple instances.
+     */
+    @VisibleForTesting
+    fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
+        if (componentName == null || componentName.packageName == null) {
+            // TODO(b/262864589): Handle empty component case
+            return false
+        }
+
+        // Check the pre-defined allow list
+        val packageName = componentName.packageName
+        for (pkg in staticAppsSupportingMultiInstance) {
+            if (pkg == packageName) {
+                KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance",
+                    packageName)
+                return true
+            }
+        }
+
+        // Check the activity property first
+        try {
+            val activityProp = packageManager.getProperty(
+                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
+            // If the above call doesn't throw a NameNotFoundException, then the activity property
+            // should override the application property value
+            if (activityProp.isBoolean) {
+                KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName)
+                return activityProp.boolean
+            } else {
+                KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d",
+                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type)
+            }
+        } catch (nnfe: PackageManager.NameNotFoundException) {
+            // Not specified in the activity, fall through
+        }
+
+        // Check the application property otherwise
+        try {
+            val appProp = packageManager.getProperty(
+                PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
+            if (appProp.isBoolean) {
+                KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
+                return appProp.boolean
+            } else {
+                KtProtoLog.w(WM_SHELL,
+                    "Warning: property=%s for application=%s has non-bool type=%d",
+                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type)
+            }
+        } catch (nnfe: PackageManager.NameNotFoundException) {
+            // Not specified in either application or activity
+        }
+        return false
+    }
+
+    companion object {
+        /** Returns the component from a PendingIntent  */
+        @JvmStatic
+        fun getComponent(pendingIntent: PendingIntent?): ComponentName? {
+            return pendingIntent?.intent?.component
+        }
+
+        /** Returns the component from a shortcut  */
+        @JvmStatic
+        fun getShortcutComponent(packageName: String, shortcutId: String,
+                user: UserHandle, launcherApps: LauncherApps): ComponentName? {
+            val query = LauncherApps.ShortcutQuery()
+            query.setPackage(packageName)
+            query.setShortcutIds(Arrays.asList(shortcutId))
+            query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED)
+            val shortcuts = launcherApps.getShortcuts(query, user)
+            val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null
+            return info?.activity
+        }
+
+        /** Returns true if package names and user ids match.  */
+        @JvmStatic
+        fun samePackage(packageName1: String?, packageName2: String?,
+                userId1: Int, userId2: Int): Boolean {
+            return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index 8b3de62..b5f25433f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
 
 import android.app.PictureInPictureParams;
 import android.view.SurfaceControl;
@@ -22,7 +22,7 @@
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 
-import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
 
 /**
  * Interface that is exposed to remote callers to manipulate the Pip feature.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
index 062e3ba..b8d1966 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
 
 /**
  * Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 662f325..f9259e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -99,12 +99,6 @@
         return taskInfo != null ? taskInfo.userId : -1;
     }
 
-    /** Returns true if package names and user ids match. */
-    public static boolean samePackage(String packageName1, String packageName2,
-            int userId1, int userId2) {
-        return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2);
-    }
-
     /** Generates a common log message for split screen failures */
     public static String splitFailureMessage(String caller, String reason) {
         return "(" + caller + ") Splitscreen aborted: " + reason;
@@ -143,28 +137,4 @@
             return isLandscape;
         }
     }
-
-    /** Returns the component from a PendingIntent */
-    @Nullable
-    public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) {
-        if (pendingIntent == null || pendingIntent.getIntent() == null) {
-            return null;
-        }
-        return pendingIntent.getIntent().getComponent();
-    }
-
-    /** Returns the component from a shortcut */
-    @Nullable
-    public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId,
-            @NonNull UserHandle user, @NonNull LauncherApps launcherApps) {
-        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
-        query.setPackage(packageName);
-        query.setShortcutIds(Arrays.asList(shortcutId));
-        query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
-        List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user);
-        ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
-                ? shortcuts.get(0)
-                : null;
-        return info != null ? info.getActivity() : null;
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index d4ed017..216da07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -26,6 +26,7 @@
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
@@ -88,13 +89,14 @@
             IconProvider iconProvider,
             Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
+            MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
             Handler mainHandler,
             SystemWindows systemWindows) {
         return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
                 displayImeController, displayInsetsController, transitions, transactionPool,
-                iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
-                systemWindows);
+                iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
+                mainExecutor, mainHandler, systemWindows);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 63a912e..8b2ec0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -50,6 +50,7 @@
 import com.android.wm.shell.common.DockStateReader;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
@@ -322,6 +323,12 @@
         return Optional.of(perfHintController.getHinter());
     }
 
+    @WMSingleton
+    @Provides
+    static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
+        return new MultiInstanceHelper(context, context.getPackageManager());
+    }
+
     //
     // Back animation
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index bd9d89c..4fe79c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -47,6 +47,7 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -347,12 +348,14 @@
             LaunchAdjacentController launchAdjacentController,
             Optional<WindowDecorViewModel> windowDecorViewModel,
             Optional<DesktopTasksController> desktopTasksController,
+            MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor) {
         return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
                 displayImeController, displayInsetsController, dragAndDropController, transitions,
                 transactionPool, iconProvider, recentTasks, launchAdjacentController,
-                windowDecorViewModel, desktopTasksController, mainExecutor);
+                windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
+                multiInstanceHelper, mainExecutor);
     }
 
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 837cb99..11304ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -62,7 +62,7 @@
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.splitscreen.SplitScreenController
-import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
@@ -355,7 +355,7 @@
             splitScreenController.prepareExitSplitScreen(
                     wct,
                     splitScreenController.getStageOfTask(taskInfo.taskId),
-                    EXIT_REASON_ENTER_DESKTOP
+                    EXIT_REASON_DESKTOP_MODE
             )
             getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
                 wct.removeTask(otherTaskInfo.token)
@@ -919,19 +919,13 @@
         }
         if (inputCoordinate.x <= transitionAreaWidth) {
             releaseVisualIndicator()
-            val wct = WindowContainerTransaction()
-            addMoveToSplitChanges(wct, taskInfo)
-            splitScreenController.requestEnterSplitSelect(taskInfo, wct,
-                SPLIT_POSITION_TOP_OR_LEFT, taskBounds)
+            snapToHalfScreen(taskInfo, SnapPosition.LEFT)
             return
         }
         if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
             ?.minus(transitionAreaWidth) ?: return)) {
             releaseVisualIndicator()
-            val wct = WindowContainerTransaction()
-            addMoveToSplitChanges(wct, taskInfo)
-            splitScreenController.requestEnterSplitSelect(taskInfo, wct,
-                SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds)
+            snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
             return
         }
         // A freeform drag-move ended, remove the indicator immediately.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 05d4f53..4b12134 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -75,6 +75,8 @@
 import com.android.wm.shell.common.TabletopModeController;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.IPip;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
@@ -85,8 +87,6 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
-import com.android.wm.shell.pip.IPip;
-import com.android.wm.shell.pip.IPipAnimationListener;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipAnimationController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 05e4af3..ad29d15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,6 +26,8 @@
 public enum ShellProtoLogGroup implements IProtoLogGroup {
     // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
     // with those in the framework ProtoLogGroup
+    WM_SHELL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            Consts.TAG_WM_SHELL),
     WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM_SHELL),
     WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1b124c2..53dd981 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,25 +23,22 @@
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
 import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getComponent;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getShortcutComponent;
 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
 import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
-import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
 import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -51,7 +48,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -73,6 +69,8 @@
 
 import androidx.annotation.BinderThread;
 import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
@@ -86,6 +84,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -138,7 +137,7 @@
     public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
     public static final int EXIT_REASON_RECREATE_SPLIT = 10;
     public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
-    public static final int EXIT_REASON_ENTER_DESKTOP = 12;
+    public static final int EXIT_REASON_DESKTOP_MODE = 12;
     @IntDef(value = {
             EXIT_REASON_UNKNOWN,
             EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -152,7 +151,7 @@
             EXIT_REASON_CHILD_TASK_ENTER_PIP,
             EXIT_REASON_RECREATE_SPLIT,
             EXIT_REASON_FULLSCREEN_SHORTCUT,
-            EXIT_REASON_ENTER_DESKTOP
+            EXIT_REASON_DESKTOP_MODE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface ExitReason{}
@@ -176,7 +175,6 @@
     private final ShellTaskOrganizer mTaskOrganizer;
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
-    private final PackageManager mPackageManager;
     private final LauncherApps mLauncherApps;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     private final ShellExecutor mMainExecutor;
@@ -192,9 +190,8 @@
     private final LaunchAdjacentController mLaunchAdjacentController;
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
     private final Optional<DesktopTasksController> mDesktopTasksController;
+    private final MultiInstanceHelper mMultiInstanceHelpher;
     private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
-    // A static allow list of apps which support multi-instance
-    private final String[] mAppsSupportingMultiInstance;
 
     @VisibleForTesting
     StageCoordinator mStageCoordinator;
@@ -204,6 +201,10 @@
     private SurfaceControl mGoingToRecentsTasksLayer;
     private SurfaceControl mStartingSplitTasksLayer;
 
+    /**
+     * @param stageCoordinator if null, a stage coordinator will be created when this controller is
+     *                         initialized. Can be non-null for testing purposes.
+     */
     public SplitScreenController(Context context,
             ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
@@ -222,13 +223,14 @@
             LaunchAdjacentController launchAdjacentController,
             Optional<WindowDecorViewModel> windowDecorViewModel,
             Optional<DesktopTasksController> desktopTasksController,
+            @Nullable StageCoordinator stageCoordinator,
+            MultiInstanceHelper multiInstanceHelper,
             ShellExecutor mainExecutor) {
         mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
-        mPackageManager = context.getPackageManager();
         mLauncherApps = context.getSystemService(LauncherApps.class);
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
@@ -243,65 +245,14 @@
         mLaunchAdjacentController = launchAdjacentController;
         mWindowDecorViewModel = windowDecorViewModel;
         mDesktopTasksController = desktopTasksController;
+        mStageCoordinator = stageCoordinator;
+        mMultiInstanceHelpher = multiInstanceHelper;
         mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
         //                    override for this controller from the base module
         if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
             shellInit.addInitCallback(this::onInit, this);
         }
-
-        // TODO(255224696): Remove the config once having a way for client apps to opt-in
-        //                  multi-instances split.
-        mAppsSupportingMultiInstance = mContext.getResources()
-                .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
-    }
-
-    @VisibleForTesting
-    SplitScreenController(Context context,
-            ShellInit shellInit,
-            ShellCommandHandler shellCommandHandler,
-            ShellController shellController,
-            ShellTaskOrganizer shellTaskOrganizer,
-            SyncTransactionQueue syncQueue,
-            RootTaskDisplayAreaOrganizer rootTDAOrganizer,
-            DisplayController displayController,
-            DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController,
-            DragAndDropController dragAndDropController,
-            Transitions transitions,
-            TransactionPool transactionPool,
-            IconProvider iconProvider,
-            RecentTasksController recentTasks,
-            LaunchAdjacentController launchAdjacentController,
-            WindowDecorViewModel windowDecorViewModel,
-            DesktopTasksController desktopTasksController,
-            ShellExecutor mainExecutor,
-            StageCoordinator stageCoordinator,
-            String[] appsSupportingMultiInstance) {
-        mShellCommandHandler = shellCommandHandler;
-        mShellController = shellController;
-        mTaskOrganizer = shellTaskOrganizer;
-        mSyncQueue = syncQueue;
-        mContext = context;
-        mPackageManager = context.getPackageManager();
-        mLauncherApps = context.getSystemService(LauncherApps.class);
-        mRootTDAOrganizer = rootTDAOrganizer;
-        mMainExecutor = mainExecutor;
-        mDisplayController = displayController;
-        mDisplayImeController = displayImeController;
-        mDisplayInsetsController = displayInsetsController;
-        mDragAndDropController = dragAndDropController;
-        mTransitions = transitions;
-        mTransactionPool = transactionPool;
-        mIconProvider = iconProvider;
-        mRecentTasksOptional = Optional.of(recentTasks);
-        mLaunchAdjacentController = launchAdjacentController;
-        mWindowDecorViewModel = Optional.of(windowDecorViewModel);
-        mDesktopTasksController = Optional.of(desktopTasksController);
-        mStageCoordinator = stageCoordinator;
-        mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
-        shellInit.addInitCallback(this::onInit, this);
-        mAppsSupportingMultiInstance = appsSupportingMultiInstance;
     }
 
     public SplitScreen asSplitScreen() {
@@ -529,8 +480,8 @@
     }
 
     /** Move the specified task to fullscreen, regardless of focus state. */
-    public void moveTaskToFullscreen(int taskId) {
-        mStageCoordinator.moveTaskToFullscreen(taskId);
+    public void moveTaskToFullscreen(int taskId, int exitReason) {
+        mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
     }
 
     public boolean isLaunchToSplit(TaskInfo taskInfo) {
@@ -613,8 +564,8 @@
 
         if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
                 user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
-            if (supportsMultiInstanceSplit(getShortcutComponent(packageName, shortcutId, user,
-                    mLauncherApps))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
+                    getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else if (isSplitScreenVisible()) {
@@ -647,7 +598,7 @@
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -679,7 +630,7 @@
         final int userId1 = shortcutInfo.getUserId();
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
                 activityOptions.setApplyMultipleTaskFlagForShortcut(true);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -718,7 +669,7 @@
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
                 fillInIntent = new Intent();
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -748,7 +699,7 @@
         final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
                 setSecondIntentMultipleTask = true;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
@@ -783,7 +734,7 @@
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 fillInIntent2 = new Intent();
@@ -820,7 +771,7 @@
                 ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
         boolean setSecondIntentMultipleTask = false;
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 setSecondIntentMultipleTask = true;
@@ -882,7 +833,7 @@
             return;
         }
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
-            if (supportsMultiInstanceSplit(getComponent(intent))) {
+            if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
                 // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
                 // the split and there is no reusable background task.
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -942,66 +893,6 @@
     }
 
     /**
-     * Returns whether a specific component desires to be launched in multiple instances for
-     * split screen.
-     */
-    @VisibleForTesting
-    boolean supportsMultiInstanceSplit(@Nullable ComponentName componentName) {
-        if (componentName == null || componentName.getPackageName() == null) {
-            // TODO(b/262864589): Handle empty component case
-            return false;
-        }
-
-        // Check the pre-defined allow list
-        final String packageName = componentName.getPackageName();
-        for (int i = 0; i < mAppsSupportingMultiInstance.length; i++) {
-            if (mAppsSupportingMultiInstance[i].equals(packageName)) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                        "application=%s in allowlist supports multi-instance", packageName);
-                return true;
-            }
-        }
-
-        // Check the activity property first
-        try {
-            final PackageManager.Property activityProp = mPackageManager.getProperty(
-                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName);
-            // If the above call doesn't throw a NameNotFoundException, then the activity property
-            // should override the application property value
-            if (activityProp.isBoolean()) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                        "activity=%s supports multi-instance", componentName);
-                return activityProp.getBoolean();
-            } else {
-                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                        "Warning: property=%s for activity=%s has non-bool type=%d",
-                        PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName,
-                        activityProp.getType());
-            }
-        } catch (PackageManager.NameNotFoundException nnfe) {
-            // Not specified in the activity, fall through
-        }
-
-        // Check the application property otherwise
-        try {
-            final PackageManager.Property appProp = mPackageManager.getProperty(
-                    PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName);
-            if (appProp.isBoolean()) {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                        "application=%s supports multi-instance", packageName);
-                return appProp.getBoolean();
-            } else {
-                ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
-                        "Warning: property=%s for application=%s has non-bool type=%d",
-                        PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.getType());
-            }
-        } catch (PackageManager.NameNotFoundException nnfe) {
-            // Not specified in either application or activity
-        }
-        return false;
-    }
-
-    /**
      * Determines whether the widgetIntent needs to be modified if multiple tasks of its
      * corresponding package/app are supported. There are 4 possible paths:
      *  <li> We select a widget for second app which is the same as the first app </li>
@@ -1144,8 +1035,8 @@
                 return "CHILD_TASK_ENTER_PIP";
             case EXIT_REASON_RECREATE_SPLIT:
                 return "RECREATE_SPLIT";
-            case EXIT_REASON_ENTER_DESKTOP:
-                return "ENTER_DESKTOP";
+            case EXIT_REASON_DESKTOP_MODE:
+                return "DESKTOP_MODE";
             default:
                 return "unknown reason, reason int = " + exitReason;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index f4ab226..a0bf843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,7 +25,7 @@
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -43,7 +43,7 @@
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
-import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -194,8 +194,8 @@
                 return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
             case EXIT_REASON_FULLSCREEN_SHORTCUT:
                 return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
-            case EXIT_REASON_ENTER_DESKTOP:
-                return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+            case EXIT_REASON_DESKTOP_MODE:
+                return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
             case EXIT_REASON_UNKNOWN:
                 // Fall through
             default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 70b2f21..fa14b4c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2973,7 +2973,7 @@
     }
 
     /** Move the specified task to fullscreen, regardless of focus state. */
-    public void moveTaskToFullscreen(int taskId) {
+    public void moveTaskToFullscreen(int taskId, int exitReason) {
         boolean leftOrTop;
         if (mMainStage.containsTask(taskId)) {
             leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2982,7 +2982,7 @@
         } else {
             return;
         }
-        mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
+        mSplitLayout.flingDividerToDismiss(!leftOrTop, exitReason);
 
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index aec4d11..e330f3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -28,6 +28,7 @@
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
@@ -79,6 +80,7 @@
             IconProvider iconProvider,
             Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
+            MultiInstanceHelper multiInstanceHelper,
             ShellExecutor mainExecutor,
             Handler mainHandler,
             SystemWindows systemWindows) {
@@ -86,7 +88,7 @@
                 syncQueue, rootTDAOrganizer, displayController, displayImeController,
                 displayInsetsController, null, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
-                Optional.empty(), mainExecutor);
+                Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor);
 
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
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 7db3d38..c713a2e 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
@@ -339,7 +339,8 @@
             final int id = v.getId();
             if (id == R.id.close_window) {
                 if (isTaskInSplitScreen(mTaskId)) {
-                    mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId);
+                    mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
+                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                 } else {
                     mTaskOperations.closeTask(mTaskToken);
                 }
@@ -365,7 +366,8 @@
             } else if (id == R.id.fullscreen_button) {
                 decoration.closeHandleMenu();
                 if (isTaskInSplitScreen(mTaskId)) {
-                    mSplitScreenController.moveTaskToFullscreen(mTaskId);
+                    mSplitScreenController.moveTaskToFullscreen(mTaskId,
+                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                 } else {
                     mDesktopTasksController.ifPresent(c ->
                             c.moveToFullscreen(mTaskId));
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 7c6e69e..5c69d55 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,7 +61,6 @@
     private int mCtrlType;
     private boolean mIsResizingOrAnimatingResize;
     @Surface.Rotation private int mRotation;
-    private boolean mVeilIsVisible;
 
     public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
             DesktopModeWindowDecoration windowDecoration,
@@ -119,10 +118,9 @@
         if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
                 mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
                 mDisplayController, mDesktopWindowDecoration)) {
-            mIsResizingOrAnimatingResize = true;
-            if (!mVeilIsVisible) {
+            if (!mIsResizingOrAnimatingResize) {
                 mDesktopWindowDecoration.showResizeVeil(mRepositionTaskBounds);
-                mVeilIsVisible = true;
+                mIsResizingOrAnimatingResize = true;
             } else {
                 mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
             }
@@ -148,11 +146,10 @@
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
                 mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
-            } else if (mVeilIsVisible) {
+            } else {
                 // If bounds haven't changed, perform necessary veil reset here as startAnimation
                 // won't be called.
-                mDesktopWindowDecoration.hideResizeVeil();
-                mIsResizingOrAnimatingResize = false;
+                resetVeilIfVisible();
             }
         } else if (DragPositioningCallbackUtility.isBelowDisallowedArea(
                 mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
@@ -168,7 +165,6 @@
         mCtrlType = CTRL_TYPE_UNDEFINED;
         mTaskBoundsAtDragStart.setEmpty();
         mRepositionStartPoint.set(0, 0);
-        mVeilIsVisible = false;
         return new Rect(mRepositionTaskBounds);
     }
 
@@ -177,6 +173,13 @@
                 || (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
     }
 
+    private void resetVeilIfVisible() {
+        if (mIsResizingOrAnimatingResize) {
+            mDesktopWindowDecoration.hideResizeVeil();
+            mIsResizingOrAnimatingResize = false;
+        }
+    }
+
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
@@ -192,7 +195,7 @@
         }
 
         startTransaction.apply();
-        mDesktopWindowDecoration.hideResizeVeil();
+        resetVeilIfVisible();
         mCtrlType = CTRL_TYPE_UNDEFINED;
         finishCallback.onTransitionFinished(null);
         mIsResizingOrAnimatingResize = false;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
new file mode 100644
index 0000000..2f5fe11
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.wm.shell.common
+
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class MultiInstanceHelperTest : ShellTestCase() {
+
+    @Before
+    fun setup() {
+        assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext))
+    }
+
+    @Test
+    fun getShortcutComponent_nullShortcuts() {
+        val launcherApps = mock<LauncherApps>()
+        whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+                .thenReturn(null)
+        assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+            TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    @Test
+    fun getShortcutComponent_noShortcuts() {
+        val launcherApps = mock<LauncherApps>()
+        whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+                .thenReturn(ArrayList<ShortcutInfo>())
+        assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+            TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    @Test
+    fun getShortcutComponent_validShortcut() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
+        val launcherApps = mock<LauncherApps>()
+        whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+                .thenReturn(arrayListOf(shortcutInfo))
+        assertEquals(component, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+            TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+    }
+
+    @Test
+    fun supportsMultiInstanceSplit_inStaticAllowList() {
+        val allowList = arrayOf(TEST_PACKAGE)
+        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+    }
+
+    @Test
+    fun supportsMultiInstanceSplit_notInStaticAllowList() {
+        val allowList = arrayOf(TEST_PACKAGE)
+        val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+        val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
+        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+    }
+
+    @Test
+    @Throws(PackageManager.NameNotFoundException::class)
+    fun supportsMultiInstanceSplit_activityPropertyTrue() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val pm = mock<PackageManager>()
+        val activityProp = PackageManager.Property("", true, "", "")
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component)))
+                .thenReturn(activityProp)
+        val appProp = PackageManager.Property("", false, "", "")
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName)))
+                .thenReturn(appProp)
+
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+        // Expect activity property to override application property
+        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+    }
+
+    @Test
+    @Throws(PackageManager.NameNotFoundException::class)
+    fun supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val pm = mock<PackageManager>()
+        val activityProp = PackageManager.Property("", false, "", "")
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component)))
+                .thenReturn(activityProp)
+        val appProp = PackageManager.Property("", true, "", "")
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName)))
+                .thenReturn(appProp)
+
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+        // Expect activity property to override application property
+        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+    }
+
+    @Test
+    @Throws(PackageManager.NameNotFoundException::class)
+    fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val pm = mock<PackageManager>()
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component)))
+                .thenThrow(PackageManager.NameNotFoundException())
+        val appProp = PackageManager.Property("", true, "", "")
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName)))
+                .thenReturn(appProp)
+
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+        // Expect fall through to app property
+        assertEquals(true, helper.supportsMultiInstanceSplit(component))
+    }
+
+    @Test
+    @Throws(PackageManager.NameNotFoundException::class)
+    fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
+        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+        val pm = mock<PackageManager>()
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component)))
+                .thenThrow(PackageManager.NameNotFoundException())
+        whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+            eq(component.packageName)))
+                .thenThrow(PackageManager.NameNotFoundException())
+
+        val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+        assertEquals(false, helper.supportsMultiInstanceSplit(component))
+    }
+
+    companion object {
+        val TEST_PACKAGE = "com.android.wm.shell.common"
+        val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
+        val TEST_ACTIVITY = "TestActivity";
+        val TEST_SHORTCUT_ID = "test_shortcut_1"
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
deleted file mode 100644
index 955660c..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
+++ /dev/null
@@ -1,69 +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.wm.shell.common.split
-
-import android.content.ComponentName
-import android.content.pm.LauncherApps
-import android.content.pm.ShortcutInfo
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.wm.shell.ShellTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-
-@RunWith(AndroidJUnit4::class)
-class SplitScreenUtilsTests : ShellTestCase() {
-
-    @Test
-    fun getShortcutComponent_nullShortcuts() {
-        val launcherApps = mock(LauncherApps::class.java).also {
-            `when`(it.getShortcuts(any(), any())).thenReturn(null)
-        }
-        assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
-                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
-    }
-
-    @Test
-    fun getShortcutComponent_noShortcuts() {
-        val launcherApps = mock(LauncherApps::class.java).also {
-            `when`(it.getShortcuts(any(), any())).thenReturn(ArrayList<ShortcutInfo>())
-        }
-        assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
-                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
-    }
-
-    @Test
-    fun getShortcutComponent_validShortcut() {
-        val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
-        val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
-        val launcherApps = mock(LauncherApps::class.java).also {
-            `when`(it.getShortcuts(any(), any())).thenReturn(arrayListOf(shortcutInfo))
-        }
-        assertEquals(component, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
-                TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
-    }
-
-    companion object {
-        val TEST_PACKAGE = "com.android.wm.shell.common.split"
-        val TEST_ACTIVITY = "TestActivity";
-        val TEST_SHORTCUT_ID = "test_shortcut_1"
-    }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 79634e6..63618f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -373,7 +373,7 @@
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_FREEFORM)
         verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
-            eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+            eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
         )
     }
 
@@ -385,7 +385,7 @@
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
             .isEqualTo(WINDOWING_MODE_FREEFORM)
         verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
-            eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+            eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
         )
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 7f3bfbb..315d97e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -22,7 +22,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -37,8 +36,6 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -49,10 +46,8 @@
 import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
 
 import androidx.test.annotation.UiThreadTest;
@@ -60,7 +55,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.R;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
@@ -69,6 +63,7 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TransactionPool;
@@ -90,6 +85,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 /**
  * Tests for {@link SplitScreenController}
  */
@@ -97,10 +94,6 @@
 @RunWith(AndroidJUnit4.class)
 public class SplitScreenControllerTests extends ShellTestCase {
 
-    private static final String TEST_PACKAGE = "com.android.wm.shell.splitscreen";
-    private static final String TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.splitscreen.fake";
-    private static final String TEST_ACTIVITY = "TestActivity";
-
     @Mock ShellInit mShellInit;
     @Mock ShellCommandHandler mShellCommandHandler;
     @Mock ShellTaskOrganizer mTaskOrganizer;
@@ -119,6 +112,7 @@
     @Mock LaunchAdjacentController mLaunchAdjacentController;
     @Mock WindowDecorViewModel mWindowDecorViewModel;
     @Mock DesktopTasksController mDesktopTasksController;
+    @Mock MultiInstanceHelper mMultiInstanceHelper;
     @Captor ArgumentCaptor<Intent> mIntentCaptor;
 
     private ShellController mShellController;
@@ -128,17 +122,15 @@
     public void setup() {
         assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
         MockitoAnnotations.initMocks(this);
-        String[] appsSupportingMultiInstance = mContext.getResources()
-                .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
         mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mMainExecutor));
         mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
                 mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
                 mRootTDAOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                appsSupportingMultiInstance));
+                mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
+                Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
+                mStageCoordinator, mMultiInstanceHelper, mMainExecutor));
     }
 
     @Test
@@ -213,7 +205,7 @@
 
     @Test
     public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
-        doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -250,13 +242,13 @@
 
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
-        verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
         verify(mStageCoordinator, never()).switchSplitPosition(any());
     }
 
     @Test
     public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
-        doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+        doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
         doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
@@ -273,14 +265,14 @@
 
         mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
                 SPLIT_POSITION_TOP_OR_LEFT, null);
-        verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+        verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
         verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
                 isNull());
     }
 
     @Test
     public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
-        doReturn(false).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+        doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
         Intent startIntent = createStartIntent("startActivity");
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -298,130 +290,6 @@
     }
 
     @Test
-    public void supportsMultiInstanceSplit_inStaticAllowList() {
-        String[] allowList = { TEST_PACKAGE };
-        SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                allowList);
-        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
-        assertEquals(true, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
-    public void supportsMultiInstanceSplit_notInStaticAllowList() {
-        String[] allowList = { TEST_PACKAGE };
-        SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                allowList);
-        ComponentName component = new ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY);
-        assertEquals(false, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
-    public void supportsMultiInstanceSplit_activityPropertyTrue()
-            throws PackageManager.NameNotFoundException {
-        Context context = spy(mContext);
-        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
-        PackageManager pm = mock(PackageManager.class);
-        doReturn(pm).when(context).getPackageManager();
-        PackageManager.Property activityProp = new PackageManager.Property("", true, "", "");
-        doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-                eq(component));
-        PackageManager.Property appProp = new PackageManager.Property("", false, "", "");
-        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-                eq(component.getPackageName()));
-
-        SplitScreenController controller = new SplitScreenController(context, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                new String[0]);
-        // Expect activity property to override application property
-        assertEquals(true, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
-    public void supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue()
-            throws PackageManager.NameNotFoundException {
-        Context context = spy(mContext);
-        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
-        PackageManager pm = mock(PackageManager.class);
-        doReturn(pm).when(context).getPackageManager();
-        PackageManager.Property activityProp = new PackageManager.Property("", false, "", "");
-        doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-                eq(component));
-        PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
-        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-                eq(component.getPackageName()));
-
-        SplitScreenController controller = new SplitScreenController(context, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                new String[0]);
-        // Expect activity property to override application property
-        assertEquals(false, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
-    public void supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue()
-            throws PackageManager.NameNotFoundException {
-        Context context = spy(mContext);
-        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
-        PackageManager pm = mock(PackageManager.class);
-        doReturn(pm).when(context).getPackageManager();
-        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
-                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
-        PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
-        doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
-                eq(component.getPackageName()));
-
-        SplitScreenController controller = new SplitScreenController(context, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                new String[0]);
-        // Expect fall through to app property
-        assertEquals(true, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
-    public void supportsMultiInstanceSplit_noActivityOrAppProperty()
-            throws PackageManager.NameNotFoundException {
-        Context context = spy(mContext);
-        ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
-        PackageManager pm = mock(PackageManager.class);
-        doReturn(pm).when(context).getPackageManager();
-        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
-                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
-        doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
-                eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component.getPackageName()));
-
-        SplitScreenController controller = new SplitScreenController(context, mShellInit,
-                mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
-                mRootTDAOrganizer, mDisplayController, mDisplayImeController,
-                mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
-                mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
-                mDesktopTasksController, mMainExecutor, mStageCoordinator,
-                new String[0]);
-        assertEquals(false, controller.supportsMultiInstanceSplit(component));
-    }
-
-    @Test
     public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
         final String reason = "test";
         when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 144b01a..4d5a33d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -20,6 +20,7 @@
 import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
 import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL;
 import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
 import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING;
 
@@ -989,17 +990,24 @@
     }
 
     /**
-     * Requests a volume change for the route asynchronously.
-     * It may have no effect if the route is currently not selected.
+     * Sets the volume for a specific route.
      *
-     * <p>This will be no-op for non-system media routers.
+     * <p>The call may have no effect if the route is currently not selected.
+     *
+     * <p>This method is only supported by {@link #getInstance(Context, String) proxy MediaRouter2
+     * instances}. Use {@link RoutingController#setVolume(int) RoutingController#setVolume(int)}
+     * instead for {@link #getInstance(Context) local MediaRouter2 instances}.</p>
      *
      * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
-     * @see #getInstance(Context, String)
-     * @hide
+     * @throws UnsupportedOperationException If called on a {@link #getInstance(Context) local
+     * router instance}.
      */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+    @FlaggedApi(FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.MEDIA_CONTENT_CONTROL,
+                Manifest.permission.MEDIA_ROUTING_CONTROL
+            })
     public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
@@ -3464,10 +3472,11 @@
             return result;
         }
 
-        /** No-op. Local routers cannot modify the volume of specific routes. */
+        /** Local routers cannot modify the volume of specific routes. */
         @Override
         public void setRouteVolume(MediaRoute2Info route, int volume) {
-            // Do nothing.
+            throw new UnsupportedOperationException(
+                    "setRouteVolume is only supported by proxy routers. See javadoc.");
             // If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2()
         }
 
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index a8e9423..e92c7b3 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -211,51 +211,40 @@
     }
 
     private static class ServiceBinder extends IMediaBrowserService.Stub {
-        private WeakReference<MediaBrowserService> mService;
+        private WeakReference<ServiceState> mServiceState;
 
-        private ServiceBinder(MediaBrowserService service) {
-            mService = new WeakReference(service);
+        private ServiceBinder(ServiceState serviceState) {
+            mServiceState = new WeakReference(serviceState);
         }
 
         @Override
         public void connect(final String pkg, final Bundle rootHints,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            MediaBrowserService service = mService.get();
-            if (service == null) {
+            ServiceState serviceState = mServiceState.get();
+            if (serviceState == null) {
                 return;
             }
 
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
-            if (!service.isValidPackage(pkg, uid)) {
+            if (!serviceState.isValidPackage(pkg, uid)) {
                 throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
                         + " package=" + pkg);
             }
 
-            service.mHandler.post(
-                    () -> service.connectOnHandler(pkg, pid, uid, rootHints, callbacks));
+            serviceState.postOnHandler(
+                    () -> serviceState.connectOnHandler(pkg, pid, uid, rootHints, callbacks));
         }
 
         @Override
         public void disconnect(final IMediaBrowserServiceCallbacks callbacks) {
-            MediaBrowserService service = mService.get();
-            if (service == null) {
+            ServiceState serviceState = mServiceState.get();
+            if (serviceState == null) {
                 return;
             }
 
-            service.mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        final IBinder b = callbacks.asBinder();
-
-                        // Clear out the old subscriptions. We are getting new ones.
-                        final ConnectionRecord old = service.mServiceState.mConnections.remove(b);
-                        if (old != null) {
-                            // TODO
-                            old.callbacks.asBinder().unlinkToDeath(old, 0);
-                        }
-                    }
-                });
+            serviceState.postOnHandler(
+                    () -> serviceState.removeConnectionRecordOnHandler(callbacks));
         }
 
         @Override
@@ -266,13 +255,13 @@
         @Override
         public void addSubscription(final String id, final IBinder token, final Bundle options,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            MediaBrowserService service = mService.get();
-            if (service == null) {
+            ServiceState serviceState = mServiceState.get();
+            if (serviceState == null) {
                 return;
             }
 
-            service.mHandler.post(
-                    () -> service.addSubscriptionOnHandler(id, callbacks, token, options));
+            serviceState.postOnHandler(
+                    () -> serviceState.addSubscriptionOnHandler(id, callbacks, token, options));
         }
 
         @Override
@@ -284,14 +273,14 @@
         @Override
         public void removeSubscription(final String id, final IBinder token,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            MediaBrowserService service = mService.get();
-            if (service == null) {
+            ServiceState serviceState = mServiceState.get();
+            if (serviceState == null) {
                 return;
             }
 
-            service.mHandler.post(
+            serviceState.postOnHandler(
                     () -> {
-                        if (!service.removeSubscriptionOnHandler(id, callbacks, token)) {
+                        if (!serviceState.removeSubscriptionOnHandler(id, callbacks, token)) {
                             Log.w(TAG, "removeSubscription for id with no subscription: " + id);
                         }
                     });
@@ -300,20 +289,20 @@
         @Override
         public void getMediaItem(final String mediaId, final ResultReceiver receiver,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            MediaBrowserService service = mService.get();
-            if (service == null) {
+            ServiceState serviceState = mServiceState.get();
+            if (serviceState == null) {
                 return;
             }
 
-            service.mHandler.post(
-                    () -> service.performLoadItemOnHandler(mediaId, callbacks, receiver));
+            serviceState.postOnHandler(
+                    () -> serviceState.performLoadItemOnHandler(mediaId, callbacks, receiver));
         }
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
-        mServiceState.mBinder = new ServiceBinder(this);
+        mServiceState.mBinder = new ServiceBinder(mServiceState);
     }
 
     @Override
@@ -448,7 +437,7 @@
             throw new IllegalStateException("The session token has already been set.");
         }
         mServiceState.mSession = token;
-        mHandler.post(() -> notifySessionTokenInitializedOnHandler(token));
+        mHandler.post(() -> mServiceState.notifySessionTokenInitializedOnHandler(token));
     }
 
     /**
@@ -526,268 +515,7 @@
         if (parentId == null) {
             throw new IllegalArgumentException("parentId cannot be null in notifyChildrenChanged");
         }
-        mHandler.post(() -> notifyChildrenChangeOnHandler(parentId, options));
-    }
-
-    /**
-     * Return whether the given package is one of the ones that is owned by the uid.
-     */
-    private boolean isValidPackage(String pkg, int uid) {
-        if (pkg == null) {
-            return false;
-        }
-        final PackageManager pm = getPackageManager();
-        final String[] packages = pm.getPackagesForUid(uid);
-        final int N = packages.length;
-        for (int i = 0; i < N; i++) {
-            if (packages[i].equals(pkg)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void notifySessionTokenInitializedOnHandler(MediaSession.Token token) {
-        Iterator<ConnectionRecord> iter = mServiceState.mConnections.values().iterator();
-        while (iter.hasNext()) {
-            ConnectionRecord connection = iter.next();
-            try {
-                connection.callbacks.onConnect(
-                        connection.root.getRootId(), token, connection.root.getExtras());
-            } catch (RemoteException e) {
-                Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
-                iter.remove();
-            }
-        }
-    }
-
-    private void notifyChildrenChangeOnHandler(final String parentId, final Bundle options) {
-        for (IBinder binder : mServiceState.mConnections.keySet()) {
-            ConnectionRecord connection = mServiceState.mConnections.get(binder);
-            List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId);
-            if (callbackList != null) {
-                for (Pair<IBinder, Bundle> callback : callbackList) {
-                    if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
-                        performLoadChildrenOnHandler(parentId, connection, callback.second);
-                    }
-                }
-            }
-        }
-    }
-
-    /** Save the subscription and if it is a new subscription send the results. */
-    private void addSubscriptionOnHandler(
-            String id, IMediaBrowserServiceCallbacks callbacks, IBinder token, Bundle options) {
-        IBinder b = callbacks.asBinder();
-        // Get the record for the connection
-        ConnectionRecord connection = mServiceState.mConnections.get(b);
-        if (connection == null) {
-            Log.w(TAG, "addSubscription for callback that isn't registered id=" + id);
-            return;
-        }
-
-        // Save the subscription
-        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
-        if (callbackList == null) {
-            callbackList = new ArrayList<>();
-        }
-        for (Pair<IBinder, Bundle> callback : callbackList) {
-            if (token == callback.first
-                    && MediaBrowserUtils.areSameOptions(options, callback.second)) {
-                return;
-            }
-        }
-        callbackList.add(new Pair<>(token, options));
-        connection.subscriptions.put(id, callbackList);
-        // send the results
-        performLoadChildrenOnHandler(id, connection, options);
-    }
-
-    private void connectOnHandler(
-            String pkg,
-            int pid,
-            int uid,
-            Bundle rootHints,
-            IMediaBrowserServiceCallbacks callbacks) {
-        IBinder b = callbacks.asBinder();
-        // Clear out the old subscriptions. We are getting new ones.
-        mServiceState.mConnections.remove(b);
-
-        // Temporarily sets a placeholder ConnectionRecord to make getCurrentBrowserInfo() work in
-        // onGetRoot().
-        mServiceState.mCurConnection =
-                new ConnectionRecord(
-                        /* service= */ this, pkg, pid, uid, rootHints, callbacks, /* root= */ null);
-        BrowserRoot root = onGetRoot(pkg, uid, rootHints);
-        mServiceState.mCurConnection = null;
-
-        // If they didn't return something, don't allow this client.
-        if (root == null) {
-            Log.i(TAG, "No root for client " + pkg + " from service " + getClass().getName());
-            try {
-                callbacks.onConnectFailed();
-            } catch (RemoteException ex) {
-                Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. pkg=" + pkg);
-            }
-        } else {
-            try {
-                ConnectionRecord connection =
-                        new ConnectionRecord(
-                                /* service= */ this, pkg, pid, uid, rootHints, callbacks, root);
-                mServiceState.mConnections.put(b, connection);
-                b.linkToDeath(connection, /* flags= */ 0);
-                if (mServiceState.mSession != null) {
-                    callbacks.onConnect(
-                            connection.root.getRootId(),
-                            mServiceState.mSession,
-                            connection.root.getExtras());
-                }
-            } catch (RemoteException ex) {
-                Log.w(TAG, "Calling onConnect() failed. Dropping client. pkg=" + pkg);
-                mServiceState.mConnections.remove(b);
-            }
-        }
-    }
-
-    /** Remove the subscription. */
-    private boolean removeSubscriptionOnHandler(
-            String id, IMediaBrowserServiceCallbacks callbacks, IBinder token) {
-        final IBinder b = callbacks.asBinder();
-
-        ConnectionRecord connection = mServiceState.mConnections.get(b);
-        if (connection == null) {
-            Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id);
-            return true;
-        }
-
-        if (token == null) {
-            return connection.subscriptions.remove(id) != null;
-        }
-        boolean removed = false;
-        List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
-        if (callbackList != null) {
-            Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
-            while (iter.hasNext()) {
-                if (token == iter.next().first) {
-                    removed = true;
-                    iter.remove();
-                }
-            }
-            if (callbackList.isEmpty()) {
-                connection.subscriptions.remove(id);
-            }
-        }
-        return removed;
-    }
-
-    /**
-     * Call onLoadChildren and then send the results back to the connection.
-     *
-     * <p>Callers must make sure that this connection is still connected.
-     */
-    private void performLoadChildrenOnHandler(
-            final String parentId, final ConnectionRecord connection, final Bundle options) {
-        final Result<List<MediaBrowser.MediaItem>> result =
-                new Result<>(parentId) {
-                    @Override
-                    void onResultSent(List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
-                        if (mServiceState.mConnections.get(connection.callbacks.asBinder())
-                                != connection) {
-                            if (DBG) {
-                                Log.d(
-                                        TAG,
-                                        "Not sending onLoadChildren result for connection that has"
-                                                + " been disconnected. pkg="
-                                                + connection.pkg
-                                                + " id="
-                                                + parentId);
-                            }
-                            return;
-                        }
-
-                        List<MediaBrowser.MediaItem> filteredList =
-                                (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
-                                        ? MediaBrowserUtils.applyPagingOptions(list, options)
-                                        : list;
-                        ParceledListSlice<MediaBrowser.MediaItem> pls = null;
-                        if (filteredList != null) {
-                            pls = new ParceledListSlice<>(filteredList);
-                            // Limit the size of initial Parcel to prevent binder buffer overflow
-                            // as onLoadChildren is an async binder call.
-                            pls.setInlineCountLimit(1);
-                        }
-                        try {
-                            connection.callbacks.onLoadChildren(parentId, pls, options);
-                        } catch (RemoteException ex) {
-                            // The other side is in the process of crashing.
-                            Log.w(
-                                    TAG,
-                                    "Calling onLoadChildren() failed for id="
-                                            + parentId
-                                            + " package="
-                                            + connection.pkg);
-                        }
-                    }
-                };
-
-        mServiceState.mCurConnection = connection;
-        if (options == null) {
-            onLoadChildren(parentId, result);
-        } else {
-            onLoadChildren(parentId, result, options);
-        }
-        mServiceState.mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onLoadChildren must call detach() or sendResult()"
-                    + " before returning for package=" + connection.pkg + " id=" + parentId);
-        }
-    }
-
-    private void performLoadItemOnHandler(
-            String itemId, IMediaBrowserServiceCallbacks callbacks, final ResultReceiver receiver) {
-        final IBinder b = callbacks.asBinder();
-        ConnectionRecord connection = mServiceState.mConnections.get(b);
-        if (connection == null) {
-            Log.w(TAG, "getMediaItem for callback that isn't registered id=" + itemId);
-            return;
-        }
-
-        final Result<MediaBrowser.MediaItem> result =
-                new Result<>(itemId) {
-                    @Override
-                    void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
-                        if (mServiceState.mConnections.get(connection.callbacks.asBinder())
-                                != connection) {
-                            if (DBG) {
-                                Log.d(
-                                        TAG,
-                                        "Not sending onLoadItem result for connection that has"
-                                                + " been disconnected. pkg="
-                                                + connection.pkg
-                                                + " id="
-                                                + itemId);
-                            }
-                            return;
-                        }
-                        if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
-                            receiver.send(RESULT_ERROR, null);
-                            return;
-                        }
-                        Bundle bundle = new Bundle();
-                        bundle.putParcelable(KEY_MEDIA_ITEM, item);
-                        receiver.send(RESULT_OK, bundle);
-                    }
-                };
-
-        mServiceState.mCurConnection = connection;
-        onLoadItem(itemId, result);
-        mServiceState.mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onLoadItem must call detach() or sendResult()"
-                    + " before returning for id=" + itemId);
-        }
+        mHandler.post(() -> mServiceState.notifyChildrenChangeOnHandler(parentId, options));
     }
 
     /**
@@ -885,7 +613,7 @@
      * service. This allows us to put the service in a valid state once the session is released
      * (which is an irrecoverable invalid state). More details about this in b/185136506.
      */
-    private static class ServiceState {
+    private class ServiceState {
 
         // Fields accessed from any caller thread.
         @Nullable private MediaSession.Token mSession;
@@ -894,5 +622,292 @@
         // Fields accessed from mHandler only.
         @NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
         @Nullable private ConnectionRecord mCurConnection;
+
+        public void postOnHandler(Runnable runnable) {
+            mHandler.post(runnable);
+        }
+
+        public void removeConnectionRecordOnHandler(IMediaBrowserServiceCallbacks callbacks) {
+            IBinder b = callbacks.asBinder();
+            // Clear out the old subscriptions. We are getting new ones.
+            ConnectionRecord old = mConnections.remove(b);
+            if (old != null) {
+                old.callbacks.asBinder().unlinkToDeath(old, 0);
+            }
+        }
+
+        public void notifySessionTokenInitializedOnHandler(MediaSession.Token token) {
+            Iterator<ConnectionRecord> iter = mConnections.values().iterator();
+            while (iter.hasNext()) {
+                ConnectionRecord connection = iter.next();
+                try {
+                    connection.callbacks.onConnect(
+                            connection.root.getRootId(), token, connection.root.getExtras());
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
+                    iter.remove();
+                }
+            }
+        }
+
+        public void notifyChildrenChangeOnHandler(String parentId, Bundle options) {
+            for (IBinder binder : mConnections.keySet()) {
+                ConnectionRecord connection = mConnections.get(binder);
+                List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(parentId);
+                if (callbackList != null) {
+                    for (Pair<IBinder, Bundle> callback : callbackList) {
+                        if (MediaBrowserUtils.hasDuplicatedItems(options, callback.second)) {
+                            performLoadChildrenOnHandler(parentId, connection, callback.second);
+                        }
+                    }
+                }
+            }
+        }
+
+        /** Save the subscription and if it is a new subscription send the results. */
+        public void addSubscriptionOnHandler(
+                String id, IMediaBrowserServiceCallbacks callbacks, IBinder token, Bundle options) {
+            IBinder b = callbacks.asBinder();
+            // Get the record for the connection
+            ConnectionRecord connection = mConnections.get(b);
+            if (connection == null) {
+                Log.w(TAG, "addSubscription for callback that isn't registered id=" + id);
+                return;
+            }
+
+            // Save the subscription
+            List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+            if (callbackList == null) {
+                callbackList = new ArrayList<>();
+            }
+            for (Pair<IBinder, Bundle> callback : callbackList) {
+                if (token == callback.first
+                        && MediaBrowserUtils.areSameOptions(options, callback.second)) {
+                    return;
+                }
+            }
+            callbackList.add(new Pair<>(token, options));
+            connection.subscriptions.put(id, callbackList);
+            // send the results
+            performLoadChildrenOnHandler(id, connection, options);
+        }
+
+        public void connectOnHandler(
+                String pkg,
+                int pid,
+                int uid,
+                Bundle rootHints,
+                IMediaBrowserServiceCallbacks callbacks) {
+            IBinder b = callbacks.asBinder();
+            // Clear out the old subscriptions. We are getting new ones.
+            mConnections.remove(b);
+
+            // Temporarily sets a placeholder ConnectionRecord to make getCurrentBrowserInfo() work
+            // in onGetRoot().
+            mCurConnection =
+                    new ConnectionRecord(
+                            /* service= */ MediaBrowserService.this,
+                            pkg,
+                            pid,
+                            uid,
+                            rootHints,
+                            callbacks,
+                            /* root= */ null);
+            BrowserRoot root = onGetRoot(pkg, uid, rootHints);
+            mCurConnection = null;
+
+            // If they didn't return something, don't allow this client.
+            if (root == null) {
+                Log.i(TAG, "No root for client " + pkg + " from service " + getClass().getName());
+                try {
+                    callbacks.onConnectFailed();
+                } catch (RemoteException ex) {
+                    Log.w(TAG, "Calling onConnectFailed() failed. Ignoring. pkg=" + pkg);
+                }
+            } else {
+                try {
+                    ConnectionRecord connection =
+                            new ConnectionRecord(
+                                    /* service= */ MediaBrowserService.this,
+                                    pkg,
+                                    pid,
+                                    uid,
+                                    rootHints,
+                                    callbacks,
+                                    root);
+                    mConnections.put(b, connection);
+                    b.linkToDeath(connection, /* flags= */ 0);
+                    if (mSession != null) {
+                        callbacks.onConnect(
+                                connection.root.getRootId(), mSession, connection.root.getExtras());
+                    }
+                } catch (RemoteException ex) {
+                    Log.w(TAG, "Calling onConnect() failed. Dropping client. pkg=" + pkg);
+                    mConnections.remove(b);
+                }
+            }
+        }
+
+        /** Remove the subscription. */
+        public boolean removeSubscriptionOnHandler(
+                String id, IMediaBrowserServiceCallbacks callbacks, IBinder token) {
+            IBinder b = callbacks.asBinder();
+
+            ConnectionRecord connection = mConnections.get(b);
+            if (connection == null) {
+                Log.w(TAG, "removeSubscription for callback that isn't registered id=" + id);
+                return true;
+            }
+
+            if (token == null) {
+                return connection.subscriptions.remove(id) != null;
+            }
+            boolean removed = false;
+            List<Pair<IBinder, Bundle>> callbackList = connection.subscriptions.get(id);
+            if (callbackList != null) {
+                Iterator<Pair<IBinder, Bundle>> iter = callbackList.iterator();
+                while (iter.hasNext()) {
+                    if (token == iter.next().first) {
+                        removed = true;
+                        iter.remove();
+                    }
+                }
+                if (callbackList.isEmpty()) {
+                    connection.subscriptions.remove(id);
+                }
+            }
+            return removed;
+        }
+
+        /**
+         * Call onLoadChildren and then send the results back to the connection.
+         *
+         * <p>Callers must make sure that this connection is still connected.
+         */
+        public void performLoadChildrenOnHandler(
+                String parentId, ConnectionRecord connection, Bundle options) {
+            Result<List<MediaBrowser.MediaItem>> result =
+                    new Result<>(parentId) {
+                        @Override
+                        void onResultSent(
+                                List<MediaBrowser.MediaItem> list, @ResultFlags int flag) {
+                            if (mServiceState.mConnections.get(connection.callbacks.asBinder())
+                                    != connection) {
+                                if (DBG) {
+                                    Log.d(
+                                            TAG,
+                                            "Not sending onLoadChildren result for connection that"
+                                                    + " has been disconnected. pkg="
+                                                    + connection.pkg
+                                                    + " id="
+                                                    + parentId);
+                                }
+                                return;
+                            }
+
+                            List<MediaBrowser.MediaItem> filteredList =
+                                    (flag & RESULT_FLAG_OPTION_NOT_HANDLED) != 0
+                                            ? MediaBrowserUtils.applyPagingOptions(list, options)
+                                            : list;
+                            ParceledListSlice<MediaBrowser.MediaItem> pls = null;
+                            if (filteredList != null) {
+                                pls = new ParceledListSlice<>(filteredList);
+                                // Limit the size of initial Parcel to prevent binder buffer
+                                // overflow as onLoadChildren is an async binder call.
+                                pls.setInlineCountLimit(1);
+                            }
+                            try {
+                                connection.callbacks.onLoadChildren(parentId, pls, options);
+                            } catch (RemoteException ex) {
+                                // The other side is in the process of crashing.
+                                Log.w(
+                                        TAG,
+                                        "Calling onLoadChildren() failed for id="
+                                                + parentId
+                                                + " package="
+                                                + connection.pkg);
+                            }
+                        }
+                    };
+
+            mCurConnection = connection;
+            if (options == null) {
+                onLoadChildren(parentId, result);
+            } else {
+                onLoadChildren(parentId, result, options);
+            }
+            mCurConnection = null;
+
+            if (!result.isDone()) {
+                throw new IllegalStateException(
+                        "onLoadChildren must call detach() or sendResult()"
+                                + " before returning for package="
+                                + connection.pkg
+                                + " id="
+                                + parentId);
+            }
+        }
+
+        public void performLoadItemOnHandler(
+                String itemId,
+                IMediaBrowserServiceCallbacks callbacks,
+                ResultReceiver receiver) {
+            IBinder b = callbacks.asBinder();
+            ConnectionRecord connection = mConnections.get(b);
+            if (connection == null) {
+                Log.w(TAG, "getMediaItem for callback that isn't registered id=" + itemId);
+                return;
+            }
+
+            Result<MediaBrowser.MediaItem> result =
+                    new Result<>(itemId) {
+                        @Override
+                        void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
+                            if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+                                if (DBG) {
+                                    Log.d(
+                                            TAG,
+                                            "Not sending onLoadItem result for connection that has"
+                                                    + " been disconnected. pkg="
+                                                    + connection.pkg
+                                                    + " id="
+                                                    + itemId);
+                                }
+                                return;
+                            }
+                            if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
+                                receiver.send(RESULT_ERROR, null);
+                                return;
+                            }
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(KEY_MEDIA_ITEM, item);
+                            receiver.send(RESULT_OK, bundle);
+                        }
+                    };
+
+            mCurConnection = connection;
+            onLoadItem(itemId, result);
+            mCurConnection = null;
+
+            if (!result.isDone()) {
+                throw new IllegalStateException(
+                        "onLoadItem must call detach() or sendResult() before returning for id="
+                                + itemId);
+            }
+        }
+
+        /** Return whether the given package corresponds to the given uid. */
+        public boolean isValidPackage(String providedPackage, int uid) {
+            if (providedPackage == null) {
+                return false;
+            }
+            PackageManager pm = getPackageManager();
+            for (String packageForUid : pm.getPackagesForUid(uid)) {
+                if (packageForUid.equals(providedPackage)) {
+                    return true;
+                }
+            }
+            return false;
+        }
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 388a65d..00068bd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -1709,7 +1709,7 @@
      * <p>
      * Two images are strongly equal if and only if the data, formats, sizes,
      * and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
-     * images, the image data is not not accessible thus the data comparison is
+     * images, the image data is not accessible thus the data comparison is
      * effectively skipped as the number of planes is zero.
      * </p>
      * <p>
@@ -2049,7 +2049,7 @@
                     }
                 } else {
                     // Case 2.
-                    collector.expectEquals("Exif orientaiton should match requested orientation",
+                    collector.expectEquals("Exif orientation should match requested orientation",
                             requestedOrientation, getExifOrientationInDegree(exifOrientation,
                             collector));
                 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
index ed70ab9..5dcd1cb 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
@@ -1127,8 +1127,8 @@
      * Get aeAvailableModes and do the validation check.
      *
      * <p>Depending on the check level this class has, for WAR or COLLECT levels,
-     * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
-     * have to abort the execution even the aeMode list is invalid.</p>
+     * If the aeMode list is invalid, return an empty mode array. The caller doesn't
+     * have to abort the execution even if the aeMode list is invalid.</p>
      * @return AE available modes
      */
     public int[] getAeAvailableModesChecked() {
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3302265..35e37b2 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -277,6 +277,7 @@
     ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
     ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
     ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
+    ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
     ASurfaceTransaction_setOnComplete; # introduced=29
     ASurfaceTransaction_setOnCommit; # introduced=31
     ASurfaceTransaction_setPosition; # introduced=31
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 4b63fbf..9b1330f 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -646,6 +646,24 @@
     transaction->setExtendedRangeBrightness(surfaceControl, currentBufferRatio, desiredRatio);
 }
 
+void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTransaction,
+                                               ASurfaceControl* aSurfaceControl,
+                                               float desiredRatio) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    if (!isfinite(desiredRatio) || (desiredRatio < 1.0f && desiredRatio > 0.0f)) {
+        LOG_ALWAYS_FATAL("setDesiredHdrHeadroom, desiredRatio isn't finite && >= 1.0f or 0, got %f",
+                         desiredRatio);
+        return;
+    }
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
+}
+
 void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
                                   ASurfaceControl* aSurfaceControl,
                                   float r, float g, float b, float alpha,
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index dd2e174..a72e539 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -26,6 +26,8 @@
     method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback);
     method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+    field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 252f46f..c5b7582 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -35,6 +36,7 @@
 import android.content.Context;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.nfc.tech.MifareClassic;
 import android.nfc.tech.Ndef;
@@ -485,6 +487,25 @@
             "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
 
     /**
+     * Intent action to start a NFC resolver activity in a customized share session with list of
+     * {@link ResolveInfo}.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    @RequiresPermission(Manifest.permission.SHOW_CUSTOMIZED_RESOLVER)
+    public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+
+    /**
+     * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
+     * targets in the customized share session.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+
+    /**
      * The requested app is correctly added to the Tag intent app preference.
      *
      * @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 03f9d74..5d0847c 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -25,6 +25,7 @@
         "//frameworks/base/packages/SystemUI:__subpackages__",
         "//frameworks/libs/systemui/tracinglib:__subpackages__",
         "//platform_testing:__subpackages__",
+        "//vendor:__subpackages__",
         "//cts:__subpackages__",
     ],
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 6f34f05..2c285c8 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -301,6 +301,9 @@
         " central place, instead of reading resources directly. This is to take into account display"
         " cutouts and other special cases. "
    bug: "317199366"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
 }
 
 flag {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index c0de87a..c9b5b75 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -44,6 +44,7 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -247,6 +248,7 @@
     }
 
     @Test
+    @Ignore
     fun elementIsReusedInSameSceneAndBetweenScenes() {
         var currentScene by mutableStateOf(TestScenes.SceneA)
         var sceneCState by mutableStateOf(0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index c6327ff..c385788 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.keyguard
 
 import android.testing.TestableLooper
+import android.view.ViewGroup
 import android.view.inputmethod.InputMethodManager
 import android.widget.EditText
 import android.widget.ImageView
@@ -51,10 +52,14 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class KeyguardPasswordViewControllerTest : SysuiTestCase() {
     @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
     @Mock private lateinit var passwordEntry: EditText
+    private var passwordEntryLayoutParams =
+        ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0)
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
     @Mock lateinit var lockPatternUtils: LockPatternUtils
@@ -92,7 +97,7 @@
         whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
             .thenReturn(mock(ImageView::class.java))
         `when`(keyguardPasswordView.resources).thenReturn(context.resources)
-
+        whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams)
         val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
         val fakeFeatureFlags = FakeFeatureFlags()
         fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index f86342c..0054d13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -24,6 +24,7 @@
 
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
+import android.view.ViewGroup;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -48,13 +49,17 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
-@RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@RunWithLooper(setAsMainLooper = true)
 public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
 
     @Mock
     private KeyguardPinBasedInputView mPinBasedInputView;
     @Mock
     private PasswordTextView mPasswordEntry;
+    private final ViewGroup.LayoutParams mPasswordEntryLayoutParams =
+            new ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0);
     @Mock
     private BouncerKeyguardMessageArea mKeyguardMessageArea;
     @Mock
@@ -103,6 +108,7 @@
                 .thenReturn(mOkButton);
 
         when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
+        when(mPasswordEntry.getLayoutParams()).thenReturn(mPasswordEntryLayoutParams);
         KeyguardKeyboardInteractor keyguardKeyboardInteractor =
                 new KeyguardKeyboardInteractor(new FakeKeyboardRepository());
         FakeFeatureFlags featureFlags = new FakeFeatureFlags();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 0aca16d..6b28319 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -20,6 +20,7 @@
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
 import android.app.admin.devicePolicyManager
+import android.appwidget.AppWidgetProviderInfo
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.platform.test.annotations.DisableFlags
@@ -136,6 +137,29 @@
                 )
         }
 
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubShowsWidgetCategoriesSetByUser() =
+        testScope.runTest {
+            kosmos.fakeSettings.putIntForUser(
+                CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING,
+                AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+                PRIMARY_USER.id
+            )
+            val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+            assertThat(setting?.categories)
+                .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN)
+        }
+
+    @EnableFlags(FLAG_COMMUNAL_HUB)
+    @Test
+    fun hubShowsKeyguardWidgetsByDefault() =
+        testScope.runTest {
+            val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+            assertThat(setting?.categories)
+                .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
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 cf727cf..ddb8582 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
@@ -30,6 +30,7 @@
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
@@ -81,6 +82,7 @@
         underTest =
             CommunalEditModeViewModel(
                 kosmos.communalInteractor,
+                kosmos.communalSettingsInteractor,
                 mediaHost,
                 uiEventLogger,
                 logcatLogBuffer("CommunalEditModeViewModelTest"),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 7a78b36..9169938 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -36,6 +36,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.LockIconViewController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.biometrics.AuthController;
@@ -45,7 +46,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
@@ -91,7 +92,8 @@
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    @Mock private ShadeViewController mShadeViewController;
+    @Mock private ShadeLockscreenInteractor mShadeLockscreenInteractor;
+    @Mock private LockIconViewController mLockIconViewController;
     @Mock private View mAmbientIndicationContainer;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private AuthController mAuthController;
@@ -109,13 +111,12 @@
                 () -> mBiometricUnlockController, () -> mAssistManager, mDozeScrimController,
                 mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController,
                 mNotificationWakeUpCoordinator, mAuthController, mNotificationIconAreaController,
-                mDozeInteractor);
+                mShadeLockscreenInteractor, mDozeInteractor);
 
         mDozeServiceHost.initialize(
                 mCentralSurfaces,
                 mStatusBarKeyguardViewManager,
                 mNotificationShadeWindowViewController,
-                mShadeViewController,
                 mAmbientIndicationContainer);
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index ce03072..c3c4239 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -24,7 +24,7 @@
 
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -186,7 +186,7 @@
      * Registers the CentralSurfaces to which this Keyguard View is mounted.
      */
     void registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            ShadeViewController shadeViewController,
+            ShadeLockscreenInteractor shadeLockscreenInteractor,
             @Nullable ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
new file mode 100644
index 0000000..03f54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.model
+
+import android.appwidget.AppWidgetProviderInfo
+
+/**
+ * The widget categories to display on communal hub (where categories is a bitfield with values that
+ * match those in {@link AppWidgetProviderInfo}).
+ */
+@JvmInline
+value class CommunalWidgetCategories(
+    // The default is keyguard widgets.
+    val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
+) {
+    fun contains(category: Int) = (categories and category) == category
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 201b049..7c65d21 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,11 +18,13 @@
 
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.appwidget.AppWidgetProviderInfo
 import android.content.IntentFilter
 import android.content.pm.UserInfo
 import com.android.systemui.Flags.communalHub
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
 import com.android.systemui.communal.data.model.DisabledReason
 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
 import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
@@ -48,6 +50,12 @@
 interface CommunalSettingsRepository {
     /** A [CommunalEnabledState] for the specified user. */
     fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+
+    /**
+     * A flow that reports the widget categories to show on the hub as selected by the user in
+     * Settings.
+     */
+    fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>
 }
 
 @SysUISingleton
@@ -89,6 +97,23 @@
             .flowOn(bgDispatcher)
     }
 
+    override fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> =
+        secureSettings
+            .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_CONTENT_SETTING))
+            // Force an update
+            .onStart { emit(Unit) }
+            .map {
+                CommunalWidgetCategories(
+                    // The default is to show only keyguard widgets.
+                    secureSettings.getIntForUser(
+                        GLANCEABLE_HUB_CONTENT_SETTING,
+                        AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+                        user.id
+                    )
+                )
+            }
+            .flowOn(bgDispatcher)
+
     private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
         secureSettings
             .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
@@ -114,6 +139,7 @@
 
     companion object {
         const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+        const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
         private const val ENABLED_SETTING_DEFAULT = 1
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 0b096ce..20f60b7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.domain.interactor
 
 import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
 import com.android.systemui.communal.data.repository.CommunalSettingsRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -55,4 +56,16 @@
             .map { model -> model.enabled }
             // Start this eagerly since the value is accessed synchronously in many places.
             .stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
+    /** What widget categories to show on the hub. */
+    val communalWidgetCategories: StateFlow<Int> =
+        userInteractor.selectedUserInfo
+            .flatMapLatest { user -> repository.getWidgetCategories(user) }
+            .map { categories -> categories.categories }
+            .stateIn(
+                scope = bgScope,
+                // Start this eagerly since the value can be accessed synchronously.
+                started = SharingStarted.Eagerly,
+                initialValue = CommunalWidgetCategories().categories
+            )
 }
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 0b355cc..efbb11e 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
@@ -18,6 +18,7 @@
 
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.dagger.SysUISingleton
@@ -40,6 +41,7 @@
 @Inject
 constructor(
     private val communalInteractor: CommunalInteractor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     private val uiEventLogger: UiEventLogger,
     @CommunalLog logBuffer: LogBuffer,
@@ -84,6 +86,10 @@
         uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
     }
 
+    /** Returns the widget categories to show on communal hub. */
+    val getCommunalWidgetCategories: Int
+        get() = communalSettingsInteractor.communalWidgetCategories.value
+
     /** Sets whether edit mode is currently open */
     fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 337a98a..a5a390d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.communal.widgets
 
+import android.appwidget.AppWidgetManager
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.Bundle
@@ -137,6 +138,10 @@
                                             R.dimen.communal_widget_picker_desired_height
                                         )
                                     )
+                                    putExtra(
+                                        AppWidgetManager.EXTRA_CATEGORY_FILTER,
+                                        communalViewModel.getCommunalWidgetCategories
+                                    )
                                 }
                             )
                         } catch (e: Exception) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 641b967..c6b9952 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -153,7 +153,7 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -3529,14 +3529,14 @@
      * @return the View Controller for the Keyguard View this class is mediating.
      */
     public KeyguardViewController registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            ShadeViewController panelView,
+            ShadeLockscreenInteractor shadeLockscreenInteractor,
             @Nullable ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer, KeyguardBypassController bypassController) {
         mCentralSurfaces = centralSurfaces;
         mKeyguardViewControllerLazy.get().registerCentralSurfaces(
                 centralSurfaces,
-                panelView,
+                shadeLockscreenInteractor,
                 shadeExpansionStateManager,
                 biometricUnlockController,
                 notificationContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
index 67a20e5..dc2eeac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInLayer.kt
@@ -18,9 +18,12 @@
 
 import android.content.Context
 import android.view.View
+import android.view.ViewTreeObserver.OnPreDrawListener
 import androidx.constraintlayout.helper.widget.Layer
 
-class AodBurnInLayer(context: Context) : Layer(context) {
+class AodBurnInLayer(
+    context: Context,
+) : Layer(context) {
     // For setScale in Layer class, it stores it in mScaleX/Y and directly apply scale to
     // referenceViews instead of keeping the value in fields of View class
     // when we try to clone ConstraintSet, it will call getScaleX from View class and return 1.0
@@ -28,13 +31,32 @@
     // which cause the flicker from AOD to LS
     private var _scaleX = 1F
     private var _scaleY = 1F
+
     // As described for _scaleX and _scaleY, we have similar issue with translation
-    private var _translationX = 1F
-    private var _translationY = 1F
+    private var _translationX = 0F
+    private var _translationY = 0F
+
+    private val _predrawListener = OnPreDrawListener {
+        super.setScaleX(_scaleX)
+        super.setScaleY(_scaleY)
+        super.setTranslationX(_translationX)
+        super.setTranslationY(_translationY)
+        true
+    }
+
     // avoid adding views with same ids
     override fun addView(view: View?) {
         view?.let { if (it.id !in referencedIds) super.addView(view) }
     }
+
+    fun registerListener(rootView: View) {
+        rootView.viewTreeObserver.addOnPreDrawListener(_predrawListener)
+    }
+
+    fun unregisterListener(rootView: View) {
+        rootView.viewTreeObserver.removeOnPreDrawListener(_predrawListener)
+    }
+
     override fun setScaleX(scaleX: Float) {
         _scaleX = scaleX
         super.setScaleX(scaleX)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
index 282c495..98bebd0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodBurnInSection.kt
@@ -23,6 +23,7 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.Flags.migrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -32,6 +33,7 @@
 @Inject
 constructor(
     private val context: Context,
+    private val rootView: KeyguardRootView,
     private val clockViewModel: KeyguardClockViewModel,
 ) : KeyguardSection() {
     private lateinit var burnInLayer: AodBurnInLayer
@@ -46,6 +48,7 @@
         burnInLayer =
             AodBurnInLayer(context).apply {
                 id = R.id.burn_in_layer
+                registerListener(rootView)
                 addView(emptyView)
                 if (!migrateClocksToBlueprint()) {
                     val statusView =
@@ -70,6 +73,7 @@
     }
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
+        burnInLayer.unregisterListener(rootView)
         constraintLayout.removeView(R.id.burn_in_layer)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 54a7ca4..a22671d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -205,7 +205,7 @@
         fun getDimen(context: Context, name: String): Int {
             val res = context.packageManager.getResourcesForApplication(context.packageName)
             val id = res.getIdentifier(name, "dimen", context.packageName)
-            return res.getDimensionPixelSize(id)
+            return if (id == 0) 0 else res.getDimensionPixelSize(id)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index ab0d489..f65f376 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
+import com.google.android.material.math.MathUtils
 import kotlin.math.abs
 
 internal fun View.setRect(rect: Rect) =
@@ -59,15 +60,6 @@
         override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
         override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
         override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
-        open fun mutateBounds(
-            view: View,
-            fromVis: Int,
-            toVis: Int,
-            fromBounds: Rect,
-            toBounds: Rect,
-            fromSSBounds: Rect?,
-            toSSBounds: Rect?
-        ) {}
 
         private fun captureValues(transition: TransitionValues) {
             val view = transition.view
@@ -81,6 +73,16 @@
             transition.values[SMARTSPACE_BOUNDS] = Rect(ss.left, ss.top, ss.right, ss.bottom)
         }
 
+        open fun mutateBounds(
+            view: View,
+            fromIsVis: Boolean,
+            toIsVis: Boolean,
+            fromBounds: Rect,
+            toBounds: Rect,
+            fromSSBounds: Rect?,
+            toSSBounds: Rect?
+        ) {}
+
         override fun createAnimator(
             sceenRoot: ViewGroup,
             startValues: TransitionValues?,
@@ -88,7 +90,6 @@
         ): Animator? {
             if (startValues == null || endValues == null) return null
 
-            val fromView = startValues.view
             var fromVis = startValues.values[PROP_VISIBILITY] as Int
             var fromIsVis = fromVis == View.VISIBLE
             var fromAlpha = startValues.values[PROP_ALPHA] as Float
@@ -111,7 +112,7 @@
                 fromVis = View.INVISIBLE
             }
 
-            mutateBounds(toView, fromVis, toVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
+            mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
             if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
                 if (DEBUG) {
                     Log.w(
@@ -127,7 +128,7 @@
 
             val sendToBack = fromIsVis && !toIsVis
             fun lerp(start: Int, end: Int, fract: Float): Int =
-                (start * (1f - fract) + end * fract).toInt()
+                MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
             fun computeBounds(fract: Float): Rect =
                 Rect(
                     lerp(fromBounds.left, toBounds.left, fract),
@@ -136,9 +137,14 @@
                     lerp(fromBounds.bottom, toBounds.bottom, fract)
                 )
 
-            fun assignAnimValues(src: String, alpha: Float, fract: Float, vis: Int? = null) {
+            fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
                 val bounds = computeBounds(fract)
-                if (DEBUG) Log.i(TAG, "$src: $toView; alpha=$alpha; vis=$vis; bounds=$bounds;")
+                val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
+                if (DEBUG)
+                    Log.i(
+                        TAG,
+                        "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;"
+                    )
                 toView.setVisibility(vis ?: View.VISIBLE)
                 toView.setAlpha(alpha)
                 toView.setRect(bounds)
@@ -154,12 +160,12 @@
                 )
             }
 
-            return ValueAnimator.ofFloat(fromAlpha, toAlpha).also { anim ->
+            return ValueAnimator.ofFloat(0f, 1f).also { anim ->
                 // We enforce the animation parameters on the target view every frame using a
                 // predraw listener. This is suboptimal but prevents issues with layout passes
                 // overwriting the animation for individual frames.
                 val predrawCallback = OnPreDrawListener {
-                    assignAnimValues("predraw", anim.animatedValue as Float, anim.animatedFraction)
+                    assignAnimValues("predraw", anim.animatedFraction)
                     return@OnPreDrawListener true
                 }
 
@@ -169,16 +175,18 @@
                 anim.addListener(
                     object : AnimatorListenerAdapter() {
                         override fun onAnimationStart(anim: Animator) {
-                            assignAnimValues("start", fromAlpha, 0f)
+                            assignAnimValues("start", 0f, fromVis)
                         }
 
                         override fun onAnimationEnd(anim: Animator) {
-                            assignAnimValues("end", toAlpha, 1f, toVis)
+                            assignAnimValues("end", 1f, toVis)
                             if (sendToBack) toView.translationZ = 0f
                             toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
                         }
                     }
                 )
+
+                assignAnimValues("init", 0f, fromVis)
                 toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
             }
         }
@@ -216,13 +224,16 @@
 
         override fun mutateBounds(
             view: View,
-            fromVis: Int,
-            toVis: Int,
+            fromIsVis: Boolean,
+            toIsVis: Boolean,
             fromBounds: Rect,
             toBounds: Rect,
             fromSSBounds: Rect?,
             toSSBounds: Rect?
         ) {
+            // Move normally if clock is not changing visibility
+            if (fromIsVis == toIsVis) return
+
             fromBounds.left = toBounds.left
             fromBounds.right = toBounds.right
             if (viewModel.useLargeClock) {
@@ -271,13 +282,16 @@
 
         override fun mutateBounds(
             view: View,
-            fromVis: Int,
-            toVis: Int,
+            fromIsVis: Boolean,
+            toIsVis: Boolean,
             fromBounds: Rect,
             toBounds: Rect,
             fromSSBounds: Rect?,
             toSSBounds: Rect?
         ) {
+            // Move normally if clock is not changing visibility
+            if (fromIsVis == toIsVis) return
+
             toBounds.left = fromBounds.left
             toBounds.right = fromBounds.right
             if (!viewModel.useLargeClock) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
index 60ab40c..f601155 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
@@ -78,8 +78,8 @@
     }
 
     companion object {
-        private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"
-        private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"
+        private const val PROP_BOUNDS_LEFT = "DefaultClockSteppingTransition:boundsLeft"
+        private const val PROP_X_IN_WINDOW = "DefaultClockSteppingTransition:xInWindow"
         private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW)
         private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9a03393..95d9bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -122,6 +122,7 @@
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
@@ -906,7 +907,6 @@
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
-        lockscreenShadeTransitionController.setShadeViewController(this);
         shadeTransitionController.setShadeViewController(this);
         dynamicPrivacyController.addListener(this::onDynamicPrivacyChanged);
         quickSettingsController.setExpansionHeightListener(this::onQsSetExpansionHeightCalled);
@@ -1826,7 +1826,14 @@
     /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
     private float getLockIconPadding() {
         float lockIconPadding = 0f;
-        if (mLockIconViewController.getTop() != 0f) {
+        if (DeviceEntryUdfpsRefactor.isEnabled()) {
+            View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+                    .findViewById(R.id.device_entry_icon_view);
+            if (deviceEntryIconView != null) {
+                lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
+                    - deviceEntryIconView.getTop();
+            }
+        } else if (mLockIconViewController.getTop() != 0f) {
             lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
                     - mLockIconViewController.getTop();
         }
@@ -4156,8 +4163,7 @@
         mFixedDuration = NO_FIXED_DURATION;
     }
 
-    @Override
-    public boolean postToView(Runnable action) {
+    boolean postToView(Runnable action) {
         return mView.post(action);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index e8d9c35..ea41912 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -66,7 +66,7 @@
     private final StatusBarWindowController mStatusBarWindowController;
     private final DeviceProvisionedController mDeviceProvisionedController;
 
-    private final Lazy<ShadeViewController> mShadeViewControllerLazy;
+    private final Lazy<NotificationPanelViewController> mNpvc;
     private final Lazy<AssistManager> mAssistManagerLazy;
     private final Lazy<NotificationGutsManager> mGutsManager;
 
@@ -89,7 +89,7 @@
             DeviceProvisionedController deviceProvisionedController,
             NotificationShadeWindowController notificationShadeWindowController,
             WindowManager windowManager,
-            Lazy<ShadeViewController> shadeViewControllerLazy,
+            Lazy<NotificationPanelViewController> shadeViewControllerLazy,
             Lazy<AssistManager> assistManagerLazy,
             Lazy<NotificationGutsManager> gutsManager
     ) {
@@ -101,7 +101,7 @@
         mCommandQueue = commandQueue;
         mMainExecutor = mainExecutor;
         mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
-        mShadeViewControllerLazy = shadeViewControllerLazy;
+        mNpvc = shadeViewControllerLazy;
         mStatusBarStateController = statusBarStateController;
         mStatusBarWindowController = statusBarWindowController;
         mDeviceProvisionedController = deviceProvisionedController;
@@ -122,7 +122,7 @@
     public void instantExpandShade() {
         // Make our window larger and the panel expanded.
         makeExpandedVisible(true /* force */);
-        getShadeViewController().expand(false /* animate */);
+        getNpvc().expand(false /* animate */);
         getCommandQueue().recomputeDisableFlags(mDisplayId, false /* animate */);
     }
 
@@ -134,29 +134,29 @@
             return;
         }
         if (getNotificationShadeWindowView() != null
-                && getShadeViewController().canBeCollapsed()
+                && getNpvc().canBeCollapsed()
                 && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
             // release focus immediately to kick off focus change transition
             mNotificationShadeWindowController.setNotificationShadeFocusable(false);
 
             mNotificationShadeWindowViewController.cancelExpandHelper();
-            getShadeViewController().collapse(true, delayed, speedUpFactor);
+            getNpvc().collapse(true, delayed, speedUpFactor);
         }
     }
 
     @Override
     protected void expandToNotifications() {
-        getShadeViewController().expandToNotifications();
+        getNpvc().expandToNotifications();
     }
 
     @Override
     protected void expandToQs() {
-        getShadeViewController().expandToQs();
+        getNpvc().expandToQs();
     }
 
     @Override
     public boolean closeShadeIfOpen() {
-        if (!getShadeViewController().isFullyCollapsed()) {
+        if (!getNpvc().isFullyCollapsed()) {
             getCommandQueue().animateCollapsePanels(
                     CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
             notifyVisibilityChanged(false);
@@ -167,12 +167,12 @@
 
     @Override
     public boolean isShadeFullyOpen() {
-        return getShadeViewController().isShadeFullyExpanded();
+        return getNpvc().isShadeFullyExpanded();
     }
 
     @Override
     public boolean isExpandingOrCollapsing() {
-        return getShadeViewController().isExpandingOrCollapsing();
+        return getNpvc().isExpandingOrCollapsing();
     }
     @Override
     public void postAnimateCollapseShade() {
@@ -191,13 +191,13 @@
 
     @Override
     public void postOnShadeExpanded(Runnable executable) {
-        getShadeViewController().addOnGlobalLayoutListener(
+        getNpvc().addOnGlobalLayoutListener(
                 new ViewTreeObserver.OnGlobalLayoutListener() {
                     @Override
                     public void onGlobalLayout() {
                         if (getNotificationShadeWindowView().isVisibleToUser()) {
-                            getShadeViewController().removeOnGlobalLayoutListener(this);
-                            getShadeViewController().postToView(executable);
+                            getNpvc().removeOnGlobalLayoutListener(this);
+                            getNpvc().postToView(executable);
                         }
                     }
                 });
@@ -209,7 +209,7 @@
     }
 
     private boolean collapseShadeInternal() {
-        if (!getShadeViewController().isFullyCollapsed()) {
+        if (!getNpvc().isFullyCollapsed()) {
             // close the shade if it was open
             animateCollapseShadeForcedDelayed();
             notifyVisibilityChanged(false);
@@ -237,10 +237,10 @@
 
     @Override
     public void cancelExpansionAndCollapseShade() {
-        if (getShadeViewController().isTracking()) {
+        if (getNpvc().isTracking()) {
             mNotificationShadeWindowViewController.cancelCurrentTouch();
         }
-        if (getShadeViewController().isPanelExpanded()
+        if (getNpvc().isPanelExpanded()
                 && mStatusBarStateController.getState() == StatusBarState.SHADE) {
             animateCollapseShade();
         }
@@ -266,7 +266,7 @@
 
     @Override
     public void instantCollapseShade() {
-        getShadeViewController().instantCollapse();
+        getNpvc().instantCollapse();
         runPostCollapseActions();
     }
 
@@ -297,7 +297,7 @@
         }
 
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
-        getShadeViewController().collapse(false, false, 1.0f);
+        getNpvc().collapse(false, false, 1.0f);
 
         mExpandedVisible = false;
         notifyVisibilityChanged(false);
@@ -319,7 +319,7 @@
         notifyExpandedVisibleChanged(false);
         getCommandQueue().recomputeDisableFlags(
                 mDisplayId,
-                getShadeViewController().shouldHideStatusBarIconsWhenExpanded());
+                getNpvc().shouldHideStatusBarIconsWhenExpanded());
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -368,15 +368,15 @@
         return mNotificationShadeWindowViewController.getView();
     }
 
-    private ShadeViewController getShadeViewController() {
-        return mShadeViewControllerLazy.get();
+    private NotificationPanelViewController getNpvc() {
+        return mNpvc.get();
     }
 
     @Override
     public void start() {
         super.start();
-        getShadeViewController().setTrackingStartedListener(this::runPostCollapseActions);
-        getShadeViewController().setOpenCloseListener(
+        getNpvc().setTrackingStartedListener(this::runPostCollapseActions);
+        getNpvc().setOpenCloseListener(
                 new OpenCloseListener() {
                     @Override
                     public void onClosingFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
index f89a9c70..d393f0d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -40,6 +40,12 @@
 
     @Binds
     @SysUISingleton
+    abstract fun bindsShadeLockscreenInteractor(
+        slsi: ShadeViewControllerEmptyImpl
+    ): ShadeLockscreenInteractor
+
+    @Binds
+    @SysUISingleton
     abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt
new file mode 100644
index 0000000..a9ba6f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLockscreenInteractor.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.shade
+
+/** Allows the lockscreen to control the shade. */
+interface ShadeLockscreenInteractor {
+
+    /**
+     * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
+     * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
+     * always visible
+     *
+     * Called when `adb shell cmd statusbar expand-notifications` is executed.
+     */
+    @Deprecated("Use ShadeInteractor instead") fun expandToNotifications()
+
+    /** Returns whether the shade is expanding or collapsing itself or quick settings. */
+    val isExpandingOrCollapsing: Boolean
+
+    /**
+     * Returns whether the shade height is greater than zero (i.e. partially or fully expanded),
+     * there is a HUN, the shade is animating, or the shade is instantly expanding.
+     */
+    @Deprecated("Use ShadeInteractor instead") val isExpanded: Boolean
+
+    /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
+    fun startBouncerPreHideAnimation()
+
+    /** Called once every minute while dozing. */
+    fun dozeTimeTick()
+
+    /**
+     * Do not let the user drag the shade up and down for the current touch session. This is
+     * necessary to avoid shade expansion while/after the bouncer is dismissed.
+     */
+    @Deprecated("Not supported by scenes") fun blockExpansionForCurrentTouch()
+
+    /** Close guts, notification menus, and QS. Set scroll and overscroll to 0. */
+    fun resetViews(animate: Boolean)
+
+    /** Sets whether the screen has temporarily woken up to display notifications. */
+    @Deprecated("Not supported by scenes") fun setPulsing(pulsing: Boolean)
+
+    /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
+    fun transitionToExpandedShade(delay: Long)
+
+    /** @see ViewGroupFadeHelper.reset */
+    @Deprecated("Not supported by scenes") fun resetViewGroupFade()
+
+    /**
+     * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
+     * but not in shade locked / shade. This is used when dragging down to the full shade.
+     */
+    @Deprecated("Not supported by scenes")
+    fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int)
+
+    /** Sets the overstretch amount in raw pixels when dragging down. */
+    @Deprecated("Not supported by scenes") fun setOverStretchAmount(amount: Float)
+
+    /**
+     * Sets the alpha value to be set on the keyguard status bar.
+     *
+     * @param alpha value between 0 and 1. -1 if the value is to be reset.
+     */
+    @Deprecated("TODO(b/325072511) delete this") fun setKeyguardStatusBarAlpha(alpha: Float)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index e4d5d22..86fdcee 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
 import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
 import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractorImpl
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -94,6 +95,20 @@
                 sceneContainerOff.get()
             }
         }
+
+        @Provides
+        @SysUISingleton
+        fun provideShadeLockscreenInteractor(
+            sceneContainerFlags: SceneContainerFlags,
+            sceneContainerOn: Provider<ShadeLockscreenInteractorImpl>,
+            sceneContainerOff: Provider<NotificationPanelViewController>
+        ): ShadeLockscreenInteractor {
+            return if (sceneContainerFlags.isEnabled()) {
+                sceneContainerOn.get()
+            } else {
+                sceneContainerOff.get()
+            }
+        }
     }
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 0befb61..941c6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -26,7 +26,7 @@
  * this class. If any method in this class is needed outside of CentralSurfacesImpl, it must be
  * pulled up into ShadeViewController.
  */
-interface ShadeSurface : ShadeViewController, ShadeBackActionInteractor {
+interface ShadeSurface : ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor {
     /** Initialize objects instead of injecting to avoid circular dependencies. */
     fun initDependencies(
         centralSurfaces: CentralSurfaces,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 74035bd..44c6a82 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -37,25 +37,10 @@
     /** Animates to an expanded shade with QS expanded. If the shade starts expanded, expands QS. */
     fun expandToQs()
 
-    /**
-     * Expand shade so that notifications are visible. Non-split shade: just expanding shade or
-     * collapsing QS when they're expanded. Split shade: only expanding shade, notifications are
-     * always visible
-     *
-     * Called when `adb shell cmd statusbar expand-notifications` is executed.
-     */
-    fun expandToNotifications()
-
     /** Returns whether the shade is expanding or collapsing itself or quick settings. */
     val isExpandingOrCollapsing: Boolean
 
     /**
-     * Returns whether the shade height is greater than zero (i.e. partially or fully expanded),
-     * there is a HUN, the shade is animating, or the shade is instantly expanding.
-     */
-    val isExpanded: Boolean
-
-    /**
      * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
      * down event.
      */
@@ -101,12 +86,6 @@
     /** Returns whether status bar icons should be hidden when the shade is expanded. */
     fun shouldHideStatusBarIconsWhenExpanded(): Boolean
 
-    /**
-     * Do not let the user drag the shade up and down for the current touch session. This is
-     * necessary to avoid shade expansion while/after the bouncer is dismissed.
-     */
-    fun blockExpansionForCurrentTouch()
-
     /** Sets a listener to be notified when touch tracking begins. */
     fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
 
@@ -120,15 +99,6 @@
     /** If the latency tracker is enabled, begins tracking expand latency. */
     fun startExpandLatencyTracking()
 
-    /** Called before animating Keyguard dismissal, i.e. the animation dismissing the bouncer. */
-    fun startBouncerPreHideAnimation()
-
-    /** Called once every minute while dozing. */
-    fun dozeTimeTick()
-
-    /** Close guts, notification menus, and QS. Set scroll and overscroll to 0. */
-    fun resetViews(animate: Boolean)
-
     /** Returns the StatusBarState. */
     val barState: Int
 
@@ -145,9 +115,6 @@
      */
     fun setAlphaChangeAnimationEndAction(r: Runnable)
 
-    /** Sets whether the screen has temporarily woken up to display notifications. */
-    fun setPulsing(pulsing: Boolean)
-
     /** Sets Qs ScrimEnabled and updates QS state. */
     fun setQsScrimEnabled(qsScrimEnabled: Boolean)
 
@@ -166,32 +133,6 @@
     /** Removes a global layout listener. */
     fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
 
-    /** Posts the given runnable to the view. */
-    fun postToView(action: Runnable): Boolean
-
-    // ******* Begin Keyguard Section *********
-    /** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
-    fun transitionToExpandedShade(delay: Long)
-
-    /** @see ViewGroupFadeHelper.reset */
-    fun resetViewGroupFade()
-
-    /**
-     * Set the alpha and translationY of the keyguard elements which only show on the lockscreen,
-     * but not in shade locked / shade. This is used when dragging down to the full shade.
-     */
-    fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int)
-
-    /** Sets the overstretch amount in raw pixels when dragging down. */
-    fun setOverStretchAmount(amount: Float)
-
-    /**
-     * Sets the alpha value to be set on the keyguard status bar.
-     *
-     * @param alpha value between 0 and 1. -1 if the value is to be reset.
-     */
-    fun setKeyguardStatusBarAlpha(alpha: Float)
-
     /**
      * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
      * screen off animation controller in order to animate in AOD without "actually" fully switching
@@ -251,8 +192,6 @@
      */
     fun performHapticFeedback(constant: Int)
 
-    // ******* End Keyguard Section *********
-
     /** Returns the ShadeHeadsUpTracker. */
     val shadeHeadsUpTracker: ShadeHeadsUpTracker
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 5d966ac..7a181f1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -27,7 +27,7 @@
 
 /** Empty implementation of ShadeViewController for variants with no shade. */
 class ShadeViewControllerEmptyImpl @Inject constructor() :
-    ShadeViewController, ShadeBackActionInteractor {
+    ShadeViewController, ShadeBackActionInteractor, ShadeLockscreenInteractor {
     override fun expand(animate: Boolean) {}
     override fun expandToQs() {}
     override fun expandToNotifications() {}
@@ -70,9 +70,6 @@
     override fun updateTouchableRegion() {}
     override fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {}
     override fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {}
-    override fun postToView(action: Runnable): Boolean {
-        return false
-    }
     override fun transitionToExpandedShade(delay: Long) {}
 
     override fun resetViewGroupFade() {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
new file mode 100644
index 0000000..21a782e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.shade.domain.interactor
+
+import com.android.keyguard.LockIconViewController
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.ShadeLockscreenInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class ShadeLockscreenInteractorImpl
+@Inject
+constructor(
+    @Background private val scope: CoroutineScope,
+    shadeInteractor: ShadeInteractor,
+    private val sceneInteractor: SceneInteractor,
+    private val lockIconViewController: LockIconViewController,
+) : ShadeLockscreenInteractor {
+    override fun expandToNotifications() {
+        changeToShadeScene()
+    }
+
+    override val isExpandingOrCollapsing = shadeInteractor.isUserInteracting.value
+
+    override val isExpanded = shadeInteractor.isAnyExpanded.value
+
+    override fun startBouncerPreHideAnimation() {
+        // TODO("b/324280998") Implement replacement or delete
+    }
+
+    override fun dozeTimeTick() {
+        lockIconViewController.dozeTimeTick()
+    }
+
+    override fun blockExpansionForCurrentTouch() {
+        // TODO("b/324280998") Implement replacement or delete
+    }
+
+    override fun resetViews(animate: Boolean) {
+        // The existing comment to the only call to this claims it only calls it to collapse QS
+        changeToShadeScene()
+    }
+
+    override fun setPulsing(pulsing: Boolean) {
+        // Now handled elsewhere. Do nothing.
+    }
+    override fun transitionToExpandedShade(delay: Long) {
+        scope.launch {
+            delay(delay)
+            changeToShadeScene()
+        }
+    }
+
+    override fun resetViewGroupFade() {
+        // Now handled elsewhere. Do nothing.
+    }
+
+    override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {
+        // Now handled elsewhere. Do nothing.
+    }
+
+    override fun setOverStretchAmount(amount: Float) {
+        // Now handled elsewhere. Do nothing.
+    }
+
+    override fun setKeyguardStatusBarAlpha(alpha: Float) {
+        // TODO(b/325072511) delete this
+    }
+
+    private fun changeToShadeScene() {
+        sceneInteractor.changeScene(
+            SceneKey.Shade,
+            "ShadeLockscreenInteractorImpl.expandToNotifications",
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
index 62c9980..0b470c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
@@ -3,10 +3,10 @@
 import android.content.Context
 import android.util.IndentingPrintWriter
 import android.util.MathUtils
-import com.android.systemui.res.R
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.controls.ui.MediaHierarchyManager
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.res.R
+import com.android.systemui.shade.ShadeLockscreenInteractor
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import dagger.assisted.Assisted
@@ -18,7 +18,7 @@
 @AssistedInject
 constructor(
         private val mediaHierarchyManager: MediaHierarchyManager,
-        @Assisted private val notificationPanelController: ShadeViewController,
+        @Assisted private val shadeLockscreenInteractor: ShadeLockscreenInteractor,
         context: Context,
         configurationController: ConfigurationController,
         dumpManager: DumpManager,
@@ -72,10 +72,10 @@
         alphaProgress = MathUtils.saturate(dragDownAmount / alphaTransitionDistance)
         alpha = 1f - alphaProgress
         translationY = calculateKeyguardTranslationY(dragDownAmount)
-        notificationPanelController.setKeyguardTransitionProgress(alpha, translationY)
+        shadeLockscreenInteractor.setKeyguardTransitionProgress(alpha, translationY)
 
         statusBarAlpha = if (useSplitShade) alpha else -1f
-        notificationPanelController.setKeyguardStatusBarAlpha(statusBarAlpha)
+        shadeLockscreenInteractor.setKeyguardStatusBarAlpha(statusBarAlpha)
     }
 
     private fun calculateKeyguardTranslationY(dragDownAmount: Float): Int {
@@ -117,7 +117,7 @@
     @AssistedFactory
     fun interface Factory {
         fun create(
-            notificationPanelController: ShadeViewController
+            shadeLockscreenInteractor: ShadeLockscreenInteractor
         ): LockscreenShadeKeyguardTransitionController
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 2e71103..1dbd87e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -31,9 +31,8 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.ShadeLockscreenInteractor
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -47,6 +46,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
 import com.android.wm.shell.animation.Interpolators
+import dagger.Lazy
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -81,9 +81,9 @@
     qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory,
     private val shadeRepository: ShadeRepository,
     private val shadeInteractor: ShadeInteractor,
-    private val powerInteractor: PowerInteractor,
     private val splitShadeStateController: SplitShadeStateController,
-    private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
+    private val shadeLockscreenInteractorLazy: Lazy<ShadeLockscreenInteractor>,
+    naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
 ) : Dumpable {
     private var pulseHeight: Float = 0f
 
@@ -92,7 +92,6 @@
         private set
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
-    lateinit var shadeViewController: ShadeViewController
     lateinit var centralSurfaces: CentralSurfaces
     lateinit var qS: QS
 
@@ -165,7 +164,6 @@
     val touchHelper =
         DragDownHelper(
             falsingManager,
-            falsingCollector,
             this,
             naturalScrollingSettingObserver,
             shadeRepository,
@@ -181,7 +179,7 @@
     }
 
     private val keyguardTransitionController by lazy {
-        keyguardTransitionControllerFactory.create(shadeViewController)
+        keyguardTransitionControllerFactory.create(shadeLockscreenInteractorLazy.get())
     }
 
     private val qsTransitionController = qsTransitionControllerFactory.create { qS }
@@ -320,7 +318,7 @@
                                 true /* drag down is always an open */
                             )
                         }
-                        shadeViewController.transitionToExpandedShade(delay)
+                        shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
                         callbacks.forEach {
                             it.setTransitionToFullShadeAmount(0f, /* animated= */ true, delay)
                         }
@@ -538,7 +536,7 @@
             } else {
                 // Let's only animate notifications
                 animationHandler = { delay: Long ->
-                    shadeViewController.transitionToExpandedShade(delay)
+                    shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
                 }
             }
             goToLockedShadeInternal(expandedView, animationHandler, cancelAction = null)
@@ -661,7 +659,7 @@
      */
     private fun performDefaultGoToFullShadeAnimation(delay: Long) {
         logger.logDefaultGoToFullShadeAnimation(delay)
-        shadeViewController.transitionToExpandedShade(delay)
+        shadeLockscreenInteractorLazy.get().transitionToExpandedShade(delay)
         animateAppear(delay)
     }
 
@@ -686,7 +684,7 @@
         } else {
             pulseHeight = height
             val overflow = nsslController.setPulseHeight(height)
-            shadeViewController.setOverStretchAmount(overflow)
+            shadeLockscreenInteractorLazy.get().setOverStretchAmount(overflow)
             val transitionHeight = if (keyguardBypassController.bypassEnabled) height else 0.0f
             transitionToShadeAmountCommon(transitionHeight)
         }
@@ -760,7 +758,6 @@
  */
 class DragDownHelper(
     private val falsingManager: FalsingManager,
-    private val falsingCollector: FalsingCollector,
     private val dragDownCallback: LockscreenShadeTransitionController,
     private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver,
     private val shadeRepository: ShadeRepository,
@@ -852,7 +849,6 @@
         if (!isDraggingDown) {
             return false
         }
-        val x = event.x
         val y = event.y
         when (event.actionMasked) {
             MotionEvent.ACTION_MOVE -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b0fefdd..b772158 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -982,7 +982,6 @@
                 this,
                 mStatusBarKeyguardViewManager,
                 getNotificationShadeWindowViewController(),
-                mShadeSurface,
                 mAmbientIndicationContainer);
         updateLightRevealScrimVisibility();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 45005cb..442e43a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -41,7 +41,7 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
@@ -98,7 +98,7 @@
     private final AuthController mAuthController;
     private final NotificationIconAreaController mNotificationIconAreaController;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private ShadeViewController mNotificationPanel;
+    private final ShadeLockscreenInteractor mShadeLockscreenInteractor;
     private View mAmbientIndicationContainer;
     private CentralSurfaces mCentralSurfaces;
     private boolean mAlwaysOnSuppressed;
@@ -121,6 +121,7 @@
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             AuthController authController,
             NotificationIconAreaController notificationIconAreaController,
+            ShadeLockscreenInteractor shadeLockscreenInteractor,
             DozeInteractor dozeInteractor) {
         super();
         mDozeLog = dozeLog;
@@ -141,6 +142,7 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mAuthController = authController;
         mNotificationIconAreaController = notificationIconAreaController;
+        mShadeLockscreenInteractor = shadeLockscreenInteractor;
         mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mDozeInteractor = dozeInteractor;
     }
@@ -154,11 +156,9 @@
             CentralSurfaces centralSurfaces,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             NotificationShadeWindowViewController notificationShadeWindowViewController,
-            ShadeViewController notificationPanel,
             View ambientIndicationContainer) {
         mCentralSurfaces = centralSurfaces;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
-        mNotificationPanel = notificationPanel;
         mNotificationShadeWindowViewController = notificationShadeWindowViewController;
         mAmbientIndicationContainer = ambientIndicationContainer;
     }
@@ -290,7 +290,7 @@
 
             private void setPulsing(boolean pulsing) {
                 mStatusBarKeyguardViewManager.setPulsing(pulsing);
-                mNotificationPanel.setPulsing(pulsing);
+                mShadeLockscreenInteractor.setPulsing(pulsing);
                 mStatusBarStateController.setPulsing(pulsing);
                 mIgnoreTouchWhilePulsing = false;
                 if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
@@ -329,7 +329,7 @@
     @Override
     public void dozeTimeTick() {
         mDozeInteractor.dozeTimeTick();
-        mNotificationPanel.dozeTimeTick();
+        mShadeLockscreenInteractor.dozeTimeTick();
         mAuthController.dozeTimeTick();
         if (mAmbientIndicationContainer instanceof DozeReceiver) {
             ((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 9d70f42..29fd225 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -86,10 +86,9 @@
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionListener;
 import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -269,7 +268,7 @@
     protected LockPatternUtils mLockPatternUtils;
     protected ViewMediatorCallback mViewMediatorCallback;
     @Nullable protected CentralSurfaces mCentralSurfaces;
-    private ShadeViewController mShadeViewController;
+    private ShadeLockscreenInteractor mShadeLockscreenInteractor;
     private BiometricUnlockController mBiometricUnlockController;
     private boolean mCentralSurfacesRegistered;
 
@@ -314,7 +313,6 @@
     // Dismiss action to be launched when we stop dozing or the keyguard is gone.
     private DismissWithActionRequest mPendingWakeupAction;
     private final KeyguardStateController mKeyguardStateController;
-    private final NotificationMediaManager mMediaManager;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final DockManager mDockManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateManager;
@@ -363,7 +361,6 @@
             DockManager dockManager,
             NotificationShadeWindowController notificationShadeWindowController,
             KeyguardStateController keyguardStateController,
-            NotificationMediaManager notificationMediaManager,
             KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
             Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
             Lazy<ShadeController> shadeController,
@@ -391,7 +388,6 @@
         mNotificationShadeWindowController = notificationShadeWindowController;
         mDreamOverlayStateController = dreamOverlayStateController;
         mKeyguardStateController = keyguardStateController;
-        mMediaManager = notificationMediaManager;
         mKeyguardUpdateManager = keyguardUpdateMonitor;
         mStatusBarStateController = sysuiStatusBarStateController;
         mDockManager = dockManager;
@@ -422,7 +418,7 @@
 
     @Override
     public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
-            ShadeViewController shadeViewController,
+            ShadeLockscreenInteractor shadeLockscreenInteractor,
             ShadeExpansionStateManager shadeExpansionStateManager,
             BiometricUnlockController biometricUnlockController,
             View notificationContainer,
@@ -431,7 +427,7 @@
         mBiometricUnlockController = biometricUnlockController;
 
         mPrimaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
-        mShadeViewController = shadeViewController;
+        mShadeLockscreenInteractor = shadeLockscreenInteractor;
         if (shadeExpansionStateManager != null) {
             ShadeExpansionChangeEvent currentState =
                     shadeExpansionStateManager.addExpansionListener(this);
@@ -565,8 +561,8 @@
         // Avoid having the shade and the bouncer open at the same time over a dream.
         final boolean hideBouncerOverDream =
                 mDreamOverlayStateController.isOverlayActive()
-                        && (mShadeViewController.isExpanded()
-                        || mShadeViewController.isExpandingOrCollapsing());
+                        && (mShadeLockscreenInteractor.isExpanded()
+                        || mShadeLockscreenInteractor.isExpandingOrCollapsing());
 
         final boolean isUserTrackingStarted =
                 event.getFraction() != EXPANSION_HIDDEN && event.getTracking();
@@ -834,7 +830,7 @@
         if (mKeyguardStateController.isShowing() && !bouncerIsAnimatingAway()) {
             final boolean isOccluded = mKeyguardStateController.isOccluded();
             // Hide quick settings.
-            mShadeViewController.resetViews(/* animate= */ !isOccluded);
+            mShadeLockscreenInteractor.resetViews(/* animate= */ !isOccluded);
             // Hide bouncer and quick-quick settings.
             if (isOccluded && !mDozing) {
                 mCentralSurfaces.hideKeyguard();
@@ -1008,7 +1004,7 @@
     public void startPreHideAnimation(Runnable finishRunnable) {
         if (primaryBouncerIsShowing()) {
             mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
-            mShadeViewController.startBouncerPreHideAnimation();
+            mShadeLockscreenInteractor.startBouncerPreHideAnimation();
 
             // We update the state (which will show the keyguard) only if an animation will run on
             // the keyguard. If there is no animation, we wait before updating the state so that we
@@ -1023,12 +1019,12 @@
         } else if (finishRunnable != null) {
             finishRunnable.run();
         }
-        mShadeViewController.blockExpansionForCurrentTouch();
+        mShadeLockscreenInteractor.blockExpansionForCurrentTouch();
     }
 
     @Override
     public void blockPanelExpansionFromCurrentTouch() {
-        mShadeViewController.blockExpansionForCurrentTouch();
+        mShadeLockscreenInteractor.blockExpansionForCurrentTouch();
     }
 
     @Override
@@ -1125,7 +1121,7 @@
     public void onKeyguardFadedAway() {
         mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
                         .setKeyguardFadingAway(false), 100);
-        mShadeViewController.resetViewGroupFade();
+        mShadeLockscreenInteractor.resetViewGroupFade();
         mCentralSurfaces.finishKeyguardFadingAway();
         mBiometricUnlockController.finishKeyguardFadingAway();
     }
@@ -1208,7 +1204,7 @@
             if (hideImmediately) {
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
             } else {
-                mShadeViewController.expandToNotifications();
+                mShadeLockscreenInteractor.expandToNotifications();
             }
         }
         return;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 4dfd5a1..b3e60e3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -29,25 +29,31 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.ScreenPowerState
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
 import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import com.android.systemui.util.Compile
 import com.android.systemui.util.Utils.isDeviceFoldable
 import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
 import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.race
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.util.time.measureTimeMillis
+import java.time.Duration
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.asCoroutineDispatcher
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
 
 /**
  * [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable
@@ -70,6 +76,8 @@
 ) : CoreStartable {
 
     private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
+    private val isAodEnabled: Boolean
+        get() = keyguardInteractor.isAodAvailable.value
 
     @OptIn(ExperimentalCoroutinesApi::class)
     override fun start() {
@@ -98,8 +106,14 @@
 
                         val displaySwitchTimeMs =
                             measureTimeMillis(systemClock) {
-                                traceAsync(TAG, "displaySwitch") {
-                                    waitForDisplaySwitch(toFoldableDeviceState)
+                                try {
+                                    withTimeout(SCREEN_EVENT_TIMEOUT) {
+                                        traceAsync(TAG, "displaySwitch") {
+                                            waitForDisplaySwitch(toFoldableDeviceState)
+                                        }
+                                    }
+                                } catch (e: TimeoutCancellationException) {
+                                    Log.e(TAG, "Wait for display switch timed out")
                                 }
                             }
 
@@ -129,19 +143,19 @@
         val isTransitionEnabled =
             unfoldTransitionInteractor.isAvailable &&
                 animationStatusRepository.areAnimationsEnabled().first()
-        if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) {
-            waitForScreenTurnedOn()
-        } else {
+        if (shouldWaitForTransitionStart(toFoldableDeviceState, isTransitionEnabled)) {
             traceAsync(TAG, "waitForTransitionStart()") {
                 unfoldTransitionInteractor.waitForTransitionStart()
             }
+        } else {
+            race({ waitForScreenTurnedOn() }, { waitForGoToSleepWithScreenOff() })
         }
     }
 
-    private fun shouldWaitForScreenOn(
+    private fun shouldWaitForTransitionStart(
         toFoldableDeviceState: Int,
         isTransitionEnabled: Boolean
-    ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled)
+    ): Boolean = (toFoldableDeviceState != FOLDABLE_DEVICE_STATE_CLOSED && isTransitionEnabled)
 
     private suspend fun waitForScreenTurnedOn() {
         traceAsync(TAG, "waitForScreenTurnedOn()") {
@@ -149,19 +163,30 @@
         }
     }
 
+    private suspend fun waitForGoToSleepWithScreenOff() {
+        traceAsync(TAG, "waitForGoToSleepWithScreenOff()") {
+            powerInteractor.detailedWakefulness
+                .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP && !isAodEnabled }
+                .first()
+        }
+    }
+
     private fun getCurrentState(): Int =
         when {
             isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+            isStateScreenOff() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
             else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
         }
 
-    private fun isStateAod(): Boolean {
+    private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled)
+
+    private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled)
+
+    private fun isAsleepDueToFold(): Boolean {
         val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
-        val isAodEnabled = keyguardInteractor.isAodAvailable.value
 
         return (lastWakefulnessEvent.isAsleep() &&
-            (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) &&
-            isAodEnabled)
+            (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD))
     }
 
     private inline fun log(msg: () -> String) {
@@ -232,6 +257,7 @@
         private const val VALUE_UNKNOWN = -1
         private const val TAG = "DisplaySwitchLatency"
         private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+        private val SCREEN_EVENT_TIMEOUT = Duration.ofMillis(15000).toMillis()
 
         private const val FOLDABLE_DEVICE_STATE_UNKNOWN =
             SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d048cbe..36d4d12 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -18,6 +18,7 @@
 
 import android.testing.TestableLooper
 import android.view.View
+import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -59,7 +60,9 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class KeyguardPinViewControllerTest : SysuiTestCase() {
 
     private lateinit var objectKeyguardPINView: KeyguardPINView
@@ -90,6 +93,8 @@
     @Mock private val mEmergencyButtonController: EmergencyButtonController? = null
     private val falsingCollector: FalsingCollector = FalsingCollectorFake()
     private val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
+    private val passwordTextViewLayoutParams =
+        ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0)
     @Mock lateinit var postureController: DevicePostureController
     @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
 
@@ -104,11 +109,9 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+        `when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
             .thenReturn(keyguardMessageArea)
-        Mockito.`when`(
-                keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
-            )
+        `when`(keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java)))
             .thenReturn(keyguardMessageAreaController)
         `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
         `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
@@ -121,6 +124,7 @@
         `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
         `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         `when`(featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false)
+        `when`(passwordTextView.layoutParams).thenReturn(passwordTextViewLayoutParams)
 
         objectKeyguardPINView =
             View.inflate(mContext, R.layout.keyguard_pin_view, null)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4a2554e..9b5364e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -49,7 +49,9 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class KeyguardSimPinViewControllerTest : SysuiTestCase() {
     private lateinit var simPinView: KeyguardSimPinView
     private lateinit var underTest: KeyguardSimPinViewController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 4f46184..e71490c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -43,7 +43,9 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class KeyguardSimPukViewControllerTest : SysuiTestCase() {
     private lateinit var simPukView: KeyguardSimPukView
     private lateinit var underTest: KeyguardSimPukViewController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 08d44c1..aa7f9a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -67,12 +67,15 @@
         context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
             Utils.getStatusBarHeaderHeightKeyguard(context)
 
-    private val LARGE_CLOCK_TOP =
+    private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE =
         context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
             context.resources.getDimensionPixelSize(
                 com.android.systemui.customization.R.dimen.small_clock_padding_top
             ) +
-            context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
+            context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+
+    private val LARGE_CLOCK_TOP =
+        LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE +
             SMART_SPACE_DATE_WEATHER_HEIGHT +
             ENHANCED_SMART_SPACE_HEIGHT
 
@@ -81,36 +84,30 @@
             com.android.systemui.customization.R.dimen.small_clock_height
         )
 
+    private var DIMENSION_BY_IDENTIFIER_NAME: List<Pair<String, Int>> = listOf()
+
     @Before
     fun setup() {
+        DIMENSION_BY_IDENTIFIER_NAME =
+            listOf(
+                "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
+                "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
+            )
+
         MockitoAnnotations.initMocks(this)
         val remoteResources = mock<Resources>()
-        whenever(
-                remoteResources.getIdentifier(
-                    anyString(),
-                    eq("dimen"),
-                    anyString(),
-                )
-            )
-            .then { invocation ->
-                val name = invocation.arguments[0] as String
-                val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
-                if (index == -1) {
-                    error(
-                        "No entry for a dimension named \"$name\", please add it to the list above."
-                    )
-                }
-                index
-            }
-        whenever(
-                remoteResources.getDimensionPixelSize(
-                    anyInt(),
-                )
-            )
-            .then { invocation ->
-                val id = invocation.arguments[0] as Int
-                DIMENSION_BY_IDENTIFIER_NAME[id].second
-            }
+        whenever(remoteResources.getIdentifier(anyString(), eq("dimen"), anyString())).then {
+            invocation ->
+            val name = invocation.arguments[0] as String
+            val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
+            // increment index so that the not-found sentinel value lines up w/ what is
+            // returned by getIdentifier when a resource is not found
+            index + 1
+        }
+        whenever(remoteResources.getDimensionPixelSize(anyInt())).then { invocation ->
+            val id = invocation.arguments[0] as Int
+            DIMENSION_BY_IDENTIFIER_NAME[id - 1].second
+        }
         val packageManager = mock<PackageManager>()
         whenever(packageManager.getResourcesForApplication(anyString())).thenReturn(remoteResources)
         mContext.setMockPackageManager(packageManager)
@@ -159,6 +156,36 @@
     }
 
     @Test
+    fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade() {
+        DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+        setLargeClock(true)
+        setSplitShade(true)
+        val cs = ConstraintSet()
+        underTest.applyDefaultConstraints(cs)
+
+        val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+        assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+        val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_SPLIT_SHADE
+        assertSmallClockTop(cs, expectedSmallClockTopMargin)
+    }
+
+    @Test
+    fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade() {
+        DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+        setLargeClock(true)
+        setSplitShade(false)
+        val cs = ConstraintSet()
+        underTest.applyDefaultConstraints(cs)
+
+        val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+        assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+        val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_NON_SPLIT_SHADE
+        assertSmallClockTop(cs, expectedSmallClockTopMargin)
+    }
+
+    @Test
     fun testApplyDefaultConstraints_SmallClock_SplitShade() {
         setLargeClock(false)
         setSplitShade(true)
@@ -249,10 +276,5 @@
     companion object {
         private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
         private val ENHANCED_SMART_SPACE_HEIGHT = 11
-        private val DIMENSION_BY_IDENTIFIER_NAME =
-            listOf(
-                "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
-                "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
-            )
     }
 }
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 44b8974..992658a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -103,6 +103,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
+import com.android.systemui.keyguard.ui.view.KeyguardRootView;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -280,6 +281,8 @@
     @Mock protected UiEventLogger mUiEventLogger;
     @Mock protected LockIconViewController mLockIconViewController;
     @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
+    @Mock protected KeyguardRootView mKeyguardRootView;
+    @Mock protected View mKeyguardRootViewChild;
     @Mock protected KeyguardMediaController mKeyguardMediaController;
     @Mock protected NavigationModeController mNavigationModeController;
     @Mock protected NavigationBarController mNavigationBarController;
@@ -389,6 +392,7 @@
 
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
         mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+        mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
 
         mMainDispatcher = getMainDispatcher();
         KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
@@ -835,13 +839,13 @@
         when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
         when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
 
+        when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
+        when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+        when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
         when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
                 .thenReturn(indicationPadding);
         mNotificationPanelViewController.loadDimens();
-
-        mNotificationPanelViewController.setAmbientIndicationTop(
-                /* ambientIndicationTop= */ stackBottom - ambientPadding,
-                /* ambientTextVisible= */ true);
     }
 
     protected void triggerPositionClockAndNotifications() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 059053c..d24fe1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -203,6 +203,10 @@
         when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(5);
+
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(5);
     }
 
     @Test
@@ -216,6 +220,10 @@
         when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
+
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(0);
     }
 
     @Test
@@ -229,6 +237,10 @@
         when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
+
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(0);
     }
 
     @Test
@@ -242,6 +254,10 @@
         when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(2);
+
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(2);
     }
 
     @Test
@@ -255,6 +271,10 @@
         when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
         assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
                 .isEqualTo(0);
+
+        mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+        assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+                .isEqualTo(0);
     }
 
     @Test
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 cc79ca4..f489937 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -80,7 +80,7 @@
     @Mock private lateinit var windowManager: WindowManager
     @Mock private lateinit var assistManager: AssistManager
     @Mock private lateinit var gutsManager: NotificationGutsManager
-    @Mock private lateinit var shadeViewController: ShadeViewController
+    @Mock private lateinit var npvc: NotificationPanelViewController
     @Mock private lateinit var nswvc: NotificationShadeWindowViewController
     @Mock private lateinit var display: Display
     @Mock private lateinit var touchLog: LogBuffer
@@ -120,7 +120,7 @@
                 deviceProvisionedController,
                 notificationShadeWindowController,
                 windowManager,
-                Lazy { shadeViewController },
+                Lazy { npvc },
                 Lazy { assistManager },
                 Lazy { gutsManager },
             )
@@ -134,9 +134,9 @@
 
         // Trying to open it does nothing.
         shadeController.animateExpandShade()
-        verify(shadeViewController, never()).expandToNotifications()
+        verify(npvc, never()).expandToNotifications()
         shadeController.animateExpandQs()
-        verify(shadeViewController, never()).expand(ArgumentMatchers.anyBoolean())
+        verify(npvc, never()).expand(ArgumentMatchers.anyBoolean())
     }
 
     @Test
@@ -145,15 +145,15 @@
 
         // Can now be opened.
         shadeController.animateExpandShade()
-        verify(shadeViewController).expandToNotifications()
+        verify(npvc).expandToNotifications()
         shadeController.animateExpandQs()
-        verify(shadeViewController).expandToQs()
+        verify(npvc).expandToQs()
     }
 
     @Test
     fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() {
         // GIVEN the shade is tracking a touch
-        whenever(shadeViewController.isTracking).thenReturn(true)
+        whenever(npvc.isTracking).thenReturn(true)
 
         // WHEN cancelExpansionAndCollapseShade is called
         shadeController.cancelExpansionAndCollapseShade()
@@ -165,7 +165,7 @@
     @Test
     fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() {
         // GIVEN the shade is tracking a touch
-        whenever(shadeViewController.isTracking).thenReturn(false)
+        whenever(npvc.isTracking).thenReturn(false)
 
         // WHEN cancelExpansionAndCollapseShade is called
         shadeController.cancelExpansionAndCollapseShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
index ffde601..9ec9b69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -25,9 +25,9 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.shade.data.repository.FakeShadeRepository
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,12 +58,11 @@
         whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
 
         dragDownHelper = DragDownHelper(
-                falsingManager,
-                falsingCollector,
-                dragDownloadCallback,
-                naturalScrollingSettingObserver,
-                FakeShadeRepository(),
-                mContext,
+            falsingManager,
+            dragDownloadCallback,
+            naturalScrollingSettingObserver,
+            FakeShadeRepository(),
+            mContext,
         ).also {
             it.expandCallback = expandCallback
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 3efcf7b..0933425 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -5,10 +5,10 @@
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestModule
-import com.android.systemui.TestMocksModule
 import com.android.systemui.ExpandHelper
+import com.android.systemui.SysUITestModule
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.TestMocksModule
 import com.android.systemui.classifier.FalsingCollectorFake
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.dagger.SysUISingleton
@@ -19,7 +19,7 @@
 import com.android.systemui.plugins.qs.QS
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
-import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.ShadeLockscreenInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
@@ -60,8 +60,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private fun <T> anyObject(): T {
     return Mockito.anyObject<T>()
@@ -94,7 +94,7 @@
     @Mock lateinit var qS: QS
     @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
     @Mock lateinit var scrimController: ScrimController
-    @Mock lateinit var shadeViewController: ShadeViewController
+    @Mock lateinit var shadeLockscreenInteractor: ShadeLockscreenInteractor
     @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
     @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
     @Mock lateinit var stackscroller: NotificationStackScrollLayout
@@ -167,7 +167,7 @@
                 keyguardTransitionControllerFactory = { notificationPanelController ->
                     LockscreenShadeKeyguardTransitionController(
                         mediaHierarchyManager = mediaHierarchyManager,
-                        notificationPanelController = notificationPanelController,
+                        shadeLockscreenInteractor = notificationPanelController,
                         context = context,
                         configurationController = configurationController,
                         dumpManager = mock(),
@@ -186,13 +186,12 @@
                 qsTransitionControllerFactory = { qsTransitionController },
                 shadeRepository = testComponent.shadeRepository,
                 shadeInteractor = testComponent.shadeInteractor,
-                powerInteractor = testComponent.powerInteractor,
                 splitShadeStateController = ResourcesSplitShadeStateController(),
+                shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
                 naturalScrollingSettingObserver = naturalScrollingSettingObserver,
             )
 
         transitionController.addCallback(transitionControllerCallback)
-        transitionController.shadeViewController = shadeViewController
         transitionController.centralSurfaces = centralSurfaces
         transitionController.qS = qS
         transitionController.setStackScroller(nsslController)
@@ -286,7 +285,7 @@
     fun testGoToLockedShadeCreatesQSAnimation() {
         transitionController.goToLockedShade(null)
         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
-        verify(shadeViewController).transitionToExpandedShade(anyLong())
+        verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
         assertNotNull(transitionController.dragDownAnimator)
     }
 
@@ -294,7 +293,7 @@
     fun testGoToLockedShadeDoesntCreateQSAnimation() {
         transitionController.goToLockedShade(null, needsQSAnimation = false)
         verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
-        verify(shadeViewController).transitionToExpandedShade(anyLong())
+        verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
         assertNull(transitionController.dragDownAnimator)
     }
 
@@ -302,7 +301,7 @@
     fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
         enableSplitShade()
         transitionController.goToLockedShade(null, needsQSAnimation = true)
-        verify(shadeViewController).transitionToExpandedShade(anyLong())
+        verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
         assertNotNull(transitionController.dragDownAnimator)
     }
 
@@ -358,7 +357,7 @@
     fun setDragAmount_setsKeyguardTransitionProgress() {
         transitionController.dragDownAmount = 10f
 
-        verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), anyInt())
+        verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
     }
 
     @Test
@@ -370,7 +369,7 @@
         transitionController.dragDownAmount = 10f
 
         val expectedAlpha = 1 - 10f / alphaDistance
-        verify(shadeViewController).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
+        verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
     }
 
     @Test
@@ -383,7 +382,7 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(0))
+        verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
     }
 
     @Test
@@ -396,7 +395,8 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
+        verify(shadeLockscreenInteractor)
+                .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
     }
 
     @Test
@@ -416,7 +416,7 @@
                 R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
             )
         val expectedTranslation = 10f / distance * offset
-        verify(shadeViewController)
+        verify(shadeLockscreenInteractor)
             .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
     }
 
@@ -555,7 +555,7 @@
         transitionController.dragDownAmount = dragDownAmount
 
         val expectedAlpha = 1 - dragDownAmount / alphaDistance
-        verify(shadeViewController).setKeyguardStatusBarAlpha(expectedAlpha)
+        verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
     }
 
     @Test
@@ -564,7 +564,7 @@
 
         transitionController.dragDownAmount = 10f
 
-        verify(shadeViewController).setKeyguardStatusBarAlpha(-1f)
+        verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
     }
 
     private fun enableSplitShade() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 4015361..bd7406a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -90,8 +90,7 @@
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.shade.ShadeLockscreenInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -125,7 +124,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private CentralSurfaces mCentralSurfaces;
     @Mock private ViewGroup mContainer;
-    @Mock private ShadeViewController mShadeViewController;
+    @Mock private ShadeLockscreenInteractor mShadeLockscreenInteractor;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -206,7 +205,6 @@
                         mock(DockManager.class),
                         mNotificationShadeWindowController,
                         mKeyguardStateController,
-                        mock(NotificationMediaManager.class),
                         mKeyguardMessageAreaFactory,
                         Optional.of(mSysUiUnfoldComponent),
                         () -> mShadeController,
@@ -234,7 +232,7 @@
                 .thenReturn(mOnBackInvokedDispatcher);
         mStatusBarKeyguardViewManager.registerCentralSurfaces(
                 mCentralSurfaces,
-                mShadeViewController,
+                mShadeLockscreenInteractor,
                 new ShadeExpansionStateManager(),
                 mBiometricUnlockController,
                 mNotificationContainer,
@@ -715,7 +713,6 @@
                         mock(DockManager.class),
                         mock(NotificationShadeWindowController.class),
                         mKeyguardStateController,
-                        mock(NotificationMediaManager.class),
                         mKeyguardMessageAreaFactory,
                         Optional.of(mSysUiUnfoldComponent),
                         () -> mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index ee2e5ad..28adbce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -321,4 +321,38 @@
             verify(displaySwitchLatencyLogger, never()).log(any())
         }
     }
+
+    @Test
+    fun foldToScreenOff_capturesToStateAsScreenOff() {
+        testScope.runTest {
+            areAnimationEnabled.emit(true)
+            deviceState.emit(DeviceState.UNFOLDED)
+            isAodAvailable.emit(false)
+
+            displaySwitchLatencyTracker.start()
+            deviceState.emit(DeviceState.HALF_FOLDED)
+            systemClock.advanceTime(50)
+            runCurrent()
+            deviceState.emit(DeviceState.FOLDED)
+            lastWakefulnessEvent.emit(
+                WakefulnessModel(
+                    internalWakefulnessState = WakefulnessState.ASLEEP,
+                    lastSleepReason = WakeSleepReason.FOLD
+                )
+            )
+            screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+            runCurrent()
+
+            verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+            val loggedEvent = loggerArgumentCaptor.value
+            val expectedLoggedEvent =
+                DisplaySwitchLatencyEvent(
+                    latencyMs = 0,
+                    fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+                    toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+                    toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
+                )
+            assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f924134..10aab96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -246,7 +246,7 @@
 
     private fun sendFoldEvent(folded: Boolean) {
         val state = if (folded) deviceStates.folded else deviceStates.unfolded
-        foldStateListenerCaptor.value.onStateChanged(state)
+        foldStateListenerCaptor.value.onDeviceStateChanged(state)
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index c7dc0ab..ba72716 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -174,7 +174,7 @@
 
     private fun sendFoldEvent(folded: Boolean) {
         val state = if (folded) deviceStates.folded else deviceStates.unfolded
-        foldStateListenerCaptor.value.onStateChanged(state)
+        foldStateListenerCaptor.value.onDeviceStateChanged(state)
     }
 
     private fun sendScreenTurnedOnEvent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
index 01b5359..e499a3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
@@ -17,21 +17,29 @@
 package com.android.systemui.unfold.util
 
 import android.content.Context
+import android.hardware.devicestate.DeviceState
 import org.junit.Assume.assumeTrue
 
 object FoldableTestUtils {
 
     /** Finds device state for folded and unfolded. */
     fun findDeviceStates(context: Context): FoldableDeviceStates {
+        // TODO(b/325474477): Migrate clients to updated DeviceStateManager API's
         val foldedDeviceStates: IntArray = context.resources.getIntArray(
             com.android.internal.R.array.config_foldedDeviceStates)
         assumeTrue("Test should be launched on a foldable device",
             foldedDeviceStates.isNotEmpty())
 
-        val folded = foldedDeviceStates.maxOrNull()!!
-        val unfolded = folded + 1
+        val folded =
+            DeviceState(foldedDeviceStates.maxOrNull()!! /* identifier */,
+                "" /* name */,
+                emptySet() /* properties */)
+        val unfolded =
+            DeviceState(folded.identifier + 1 /* identifier */,
+                "" /* name */,
+                emptySet() /* properties */)
         return FoldableDeviceStates(folded = folded, unfolded = unfolded)
     }
 }
 
-data class FoldableDeviceStates(val folded: Int, val unfolded: Int)
\ No newline at end of file
+data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index 82e0b8e..f4acf4d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -73,7 +73,7 @@
             deviceProvisionedController,
             mock<NotificationShadeWindowController>(),
             mock<WindowManager>(),
-            { mock<ShadeViewController>() },
+            { mock<NotificationPanelViewController>() },
             { mock<AssistManager>() },
             { mock<NotificationGutsManager>() },
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
new file mode 100644
index 0000000..4221d06
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.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.shade.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.shadeLockscreenInteractor by
+    Kosmos.Fixture {
+        ShadeLockscreenInteractorImpl(
+            scope = testScope,
+            shadeInteractor = shadeInteractorImpl,
+            sceneInteractor = sceneInteractor,
+            lockIconViewController = mock(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
index 1c6ce79..81888c484 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerKosmos.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.media.controls.ui.mediaHierarchyManager
 import com.android.systemui.plugins.activityStarter
-import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
 import com.android.systemui.statusbar.notification.stack.ambientState
 import com.android.systemui.statusbar.phone.keyguardBypassController
 import com.android.systemui.statusbar.phone.lsShadeTransitionLogger
@@ -58,8 +58,8 @@
         qsTransitionControllerFactory = lockscreenShadeQsTransitionControllerFactory,
         shadeRepository = shadeRepository,
         shadeInteractor = shadeInteractor,
-        powerInteractor = powerInteractor,
         splitShadeStateController = splitShadeStateController,
+        shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
         naturalScrollingSettingObserver = naturalScrollingSettingObserver,
     )
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 7b5932b..1d5c79c 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,6 +16,8 @@
 
 package android.platform.test.ravenwood;
 
+import static org.junit.Assert.assertFalse;
+
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.os.Build;
@@ -28,12 +30,19 @@
 
 import com.android.internal.os.RuntimeInit;
 
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
 import org.junit.runner.Description;
 import org.junit.runner.RunWith;
 import org.junit.runners.model.Statement;
 
 import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executors;
@@ -183,6 +192,7 @@
     public static void validate(Statement base, Description description,
             boolean enableOptionalValidation) {
         validateTestRunner(base, description, enableOptionalValidation);
+        validateTestAnnotations(base, description, enableOptionalValidation);
     }
 
     private static void validateTestRunner(Statement base, Description description,
@@ -206,4 +216,63 @@
             }
         }
     }
+
+    private static void validateTestAnnotations(Statement base, Description description,
+            boolean enableOptionalValidation) {
+        final var testClass = description.getTestClass();
+
+        final var message = new StringBuilder();
+
+        boolean hasErrors = false;
+        for (Method m : collectMethods(testClass)) {
+            if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("test")) {
+                if (m.getAnnotation(Test.class) == null) {
+                    message.append("\nMethod " + m.getName() + "() doesn't have @Test");
+                    hasErrors = true;
+                }
+            }
+            if ("setUp".equals(m.getName())) {
+                if (m.getAnnotation(Before.class) == null) {
+                    message.append("\nMethod " + m.getName() + "() doesn't have @Before");
+                    hasErrors = true;
+                }
+                if (!Modifier.isPublic(m.getModifiers())) {
+                    message.append("\nMethod " + m.getName() + "() must be public");
+                    hasErrors = true;
+                }
+            }
+            if ("tearDown".equals(m.getName())) {
+                if (m.getAnnotation(After.class) == null) {
+                    message.append("\nMethod " + m.getName() + "() doesn't have @After");
+                    hasErrors = true;
+                }
+                if (!Modifier.isPublic(m.getModifiers())) {
+                    message.append("\nMethod " + m.getName() + "() must be public");
+                    hasErrors = true;
+                }
+            }
+        }
+        assertFalse("Problem(s) detected in class " + testClass.getCanonicalName() + ":"
+                + message, hasErrors);
+    }
+
+    /**
+     * Collect all (public or private or any) methods in a class, including inherited methods.
+     */
+    private static List<Method> collectMethods(Class<?> clazz) {
+        var ret = new ArrayList<Method>();
+        collectMethods(clazz, ret);
+        return ret;
+    }
+
+    private static void collectMethods(Class<?> clazz, List<Method> result) {
+        // Class.getMethods() only return public methods, so we need to use getDeclaredMethods()
+        // instead, and recurse.
+        for (var m : clazz.getDeclaredMethods()) {
+            result.add(m);
+        }
+        if (clazz.getSuperclass() != null) {
+            collectMethods(clazz.getSuperclass(), result);
+        }
+    }
 }
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5407af7..18e11ba 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -327,10 +327,13 @@
         synchronized (mLock) {
             // No need to enforce unlocked state when there is no caller. User can be in the
             // stopping state or removed by the time the message is processed
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_to_bytes");
             ensureGroupStateLoadedLocked(userId, false /* enforceUserUnlockingOrUnlocked */);
             userIdToBytesMapping = saveStateToByteArrayLocked(userId);
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
 
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "byte_to_disk_io");
         for (int i = 0; i < userIdToBytesMapping.size(); i++) {
             int currentProfileId = userIdToBytesMapping.keyAt(i);
             byte[] currentStateByteArray = userIdToBytesMapping.valueAt(i);
@@ -351,6 +354,7 @@
                 currentFile.failWrite(fileStream);
             }
         }
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
         return true;
     }
@@ -4787,8 +4791,10 @@
             synchronized (mLock) {
                 // No need to enforce unlocked state when there is no caller. User can be in the
                 // stopping state or removed by the time the message is processed
+                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_and_io");
                 ensureGroupStateLoadedLocked(mUserId, false /* enforceUserUnlockingOrUnlocked */ );
                 saveStateLocked(mUserId);
+                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             }
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 96c65565..d47245e 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -5108,12 +5108,13 @@
                         }
                     }
                 } else if (resultCode == FAILURE_CREDMAN_SELECTOR) {
-                    GetCredentialException exception =  resultData.getParcelable(
-                            CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
-                            GetCredentialException.class);
-                    Slog.d(TAG, "Credman bottom sheet from pinned "
-                            + "entry failed with: + " + exception.getType() + " , "
-                            + exception.getMessage());
+                    String[] exception =  resultData.getStringArray(
+                            CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION);
+                    if (exception != null && exception.length >= 2) {
+                        Slog.w(TAG, "Credman bottom sheet from pinned "
+                                + "entry failed with: + " + exception[0] + " , "
+                                + exception[1]);
+                    }
                 } else {
                     Slog.d(TAG, "Unknown resultCode from credential "
                             + "manager bottom sheet: " + resultCode);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b43f1a9..ba1f51b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -40,6 +40,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
 import static com.android.server.companion.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
 import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
 import static com.android.server.companion.PackageUtils.getPackageInfo;
 import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
@@ -871,13 +872,22 @@
         @Override
         public PendingIntent requestNotificationAccess(ComponentName component, int userId)
                 throws RemoteException {
-            String callingPackage = component.getPackageName();
+            int callingUid = getCallingUid();
+            final String callingPackage = component.getPackageName();
+
             checkCanCallNotificationApi(callingPackage, userId);
+
             if (component.flattenToString().length() > MAX_CN_LENGTH) {
                 throw new IllegalArgumentException("Component name is too long.");
             }
+
             final long identity = Binder.clearCallingIdentity();
             try {
+                if (!isRestrictedSettingsAllowed(getContext(), callingPackage, callingUid)) {
+                    Slog.e(TAG, "Side loaded app must enable restricted "
+                            + "setting before request the notification access");
+                    return null;
+                }
                 return PendingIntent.getActivityAsUser(getContext(),
                         0 /* request code */,
                         NotificationAccessConfirmationActivityContract.launcherIntent(
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index 6c77018..3aae1ec 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
 import android.companion.CompanionDeviceService;
 import android.content.ComponentName;
 import android.content.Context;
@@ -222,4 +223,15 @@
 
         return requestingPackageSignatureAllowlisted;
     }
+
+    /**
+     * Check if restricted settings is enabled for a side-loaded app.
+     */
+    public static boolean isRestrictedSettingsAllowed(
+            Context context, String packageName, int uid) {
+        final int mode = context.getSystemService(AppOpsManager.class).noteOpNoThrow(
+                AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid,
+                packageName, /* attributionTag= */ null, /* message= */ null);
+        return mode == AppOpsManager.MODE_ALLOWED;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d97731c..ff83797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -243,7 +243,7 @@
     /**
      * The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
      */
-    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+    private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
 
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 3f3540e..48bf9f4 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -216,6 +216,13 @@
         public String[] getFaceAidlInstances() {
             return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
         }
+
+        /**
+         * Allows to test with handlers.
+         */
+        public BiometricHandlerProvider getBiometricHandlerProvider() {
+            return BiometricHandlerProvider.getInstance();
+        }
     }
 
     private final class AuthServiceImpl extends IAuthService.Stub {
@@ -772,7 +779,6 @@
         }
 
         if (com.android.server.biometrics.Flags.deHidl()) {
-            Slog.d(TAG, "deHidl flag is on.");
             registerAuthenticators();
         } else {
             // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
@@ -783,10 +789,16 @@
     }
 
     private void registerAuthenticators() {
-        registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
-                mInjector.getFingerprintConfiguration(getContext()));
-        registerFaceSensors(mInjector.getFaceAidlInstances(),
-                mInjector.getFaceConfiguration(getContext()));
+        BiometricHandlerProvider handlerProvider = mInjector.getBiometricHandlerProvider();
+
+        handlerProvider.getFingerprintHandler().post(() ->
+                registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
+                        mInjector.getFingerprintConfiguration(getContext()), getContext(),
+                        mInjector.getFingerprintService()));
+        handlerProvider.getFaceHandler().post(() ->
+                registerFaceSensors(mInjector.getFaceAidlInstances(),
+                        mInjector.getFaceConfiguration(getContext()), getContext(),
+                        mInjector.getFaceService()));
         registerIrisSensors(mInjector.getIrisConfiguration(getContext()));
     }
 
@@ -837,15 +849,18 @@
         }
     }
 
-    private void registerFaceSensors(final String[] faceAidlInstances,
-            final String[] hidlConfigStrings) {
+    /**
+     * This method is invoked on {@link BiometricHandlerProvider.mFaceHandler}.
+     */
+    private static void registerFaceSensors(final String[] faceAidlInstances,
+            final String[] hidlConfigStrings, final Context context,
+            final IFaceService faceService) {
         final FaceSensorConfigurations mFaceSensorConfigurations =
                 new FaceSensorConfigurations(hidlConfigStrings != null
                         && hidlConfigStrings.length > 0);
 
         if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
-            mFaceSensorConfigurations.addHidlConfigs(
-                    hidlConfigStrings, getContext());
+            mFaceSensorConfigurations.addHidlConfigs(hidlConfigStrings, context);
         }
 
         if (faceAidlInstances != null && faceAidlInstances.length > 0) {
@@ -854,7 +869,6 @@
                             ServiceManager.waitForDeclaredService(name))));
         }
 
-        final IFaceService faceService = mInjector.getFaceService();
         if (faceService != null) {
             try {
                 faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
@@ -866,14 +880,18 @@
         }
     }
 
-    private void registerFingerprintSensors(final String[] fingerprintAidlInstances,
-            final String[] hidlConfigStrings) {
+    /**
+     * This method is invoked on {@link BiometricHandlerProvider.mFingerprintHandler}.
+     */
+    private static void registerFingerprintSensors(final String[] fingerprintAidlInstances,
+            final String[] hidlConfigStrings, final Context context,
+            final IFingerprintService fingerprintService) {
         final FingerprintSensorConfigurations mFingerprintSensorConfigurations =
                 new FingerprintSensorConfigurations(!(hidlConfigStrings != null
                         && hidlConfigStrings.length > 0));
 
         if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
-            mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext());
+            mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, context);
         }
 
         if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) {
@@ -882,7 +900,6 @@
                             ServiceManager.waitForDeclaredService(name))));
         }
 
-        final IFingerprintService fingerprintService = mInjector.getFingerprintService();
         if (fingerprintService != null) {
             try {
                 fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index b0649b9..0f01510 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -22,7 +22,6 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.os.IBinder;
 
-import com.android.server.biometrics.Flags;
 import com.android.server.biometrics.log.BiometricContext;
 import com.android.server.biometrics.log.BiometricLogger;
 import com.android.server.biometrics.log.OperationContextExt;
@@ -94,9 +93,6 @@
     }
 
     protected OperationContextExt getOperationContext() {
-        if (Flags.deHidl()) {
-            return mOperationContext;
-        }
         return getBiometricContext().updateContext(mOperationContext, isCryptoOperation());
     }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index cd064ae..38051c1 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,8 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.hardware.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
 import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
 import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
@@ -1065,7 +1065,8 @@
     }
 
     private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
-        @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+        @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
+        int mCurrentBaseState;
 
         @Override
         public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates,
@@ -1078,8 +1079,10 @@
 
         @Override
         public void onStateChanged(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier) {
-            if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier) {
+            if (identifier < MINIMUM_DEVICE_STATE_IDENTIFIER
+                    || identifier > MAXIMUM_DEVICE_STATE_IDENTIFIER) {
                 throw new IllegalArgumentException("Invalid identifier: " + identifier);
             }
 
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 65b393a..b865c1d9 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,8 +16,8 @@
 
 package com.android.server.devicestate;
 
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -133,10 +133,12 @@
          *
          * @param identifier the identifier of the new device state.
          *
-         * @throws IllegalArgumentException if the state is less than {@link MINIMUM_DEVICE_STATE}
-         * or greater than {@link MAXIMUM_DEVICE_STATE}.
+         * @throws IllegalArgumentException if the state is less than
+         * {@link MINIMUM_DEVICE_STATE_IDENTIFIER} or greater than
+         * {@link MAXIMUM_DEVICE_STATE_IDENTIFIER}.
          */
         void onStateChanged(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier);
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier);
     }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b5c51af..796d8d7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -340,6 +340,8 @@
     static final String TAG = NetworkPolicyLogger.TAG;
     private static final boolean LOGD = NetworkPolicyLogger.LOGD;
     private static final boolean LOGV = NetworkPolicyLogger.LOGV;
+    // TODO: b/304347838 - Remove once the feature is in staging.
+    private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
 
     /**
      * No opportunistic quota could be calculated from user data plan or data settings.
@@ -1061,7 +1063,8 @@
                     }
 
                     // The flag is boot-stable.
-                    mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
+                    mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK
+                            && Flags.networkBlockedForTopSleepingAndAbove();
                     if (mBackgroundNetworkRestricted) {
                         // Firewall rules and UidBlockedState will get updated in
                         // updateRulesForGlobalChangeAL below.
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index bbce26c..9db4d33 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -53,7 +53,8 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 final class PackageAbiHelperImpl implements PackageAbiHelper {
 
@@ -314,7 +315,7 @@
 
     @NonNull
     private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) {
-        Set<String> nativelySupportedAbis = new ArraySet<>();
+        List<String> nativelySupportedAbis = new ArrayList<>();
         for (int i = 0; i < supportedAbis.length; i++) {
             final String currentAbi = supportedAbis[i];
             // In presence of a native bridge this means the Abi is emulated.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
index 4b98e34..ea37d8e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerHistoricalSession.java
@@ -78,6 +78,7 @@
     private final int mSessionErrorCode;
     private final String mSessionErrorMessage;
     private final String mPreVerifiedDomains;
+    private final String mPackageName;
 
     PackageInstallerHistoricalSession(int sessionId, int userId, int originalInstallerUid,
             String originalInstallerPackageName, InstallSource installSource, int installerUid,
@@ -88,7 +89,8 @@
             String finalMessage, SessionParams params, int parentSessionId,
             int[] childSessionIds, boolean sessionApplied, boolean sessionFailed,
             boolean sessionReady, int sessionErrorCode, String sessionErrorMessage,
-            PreapprovalDetails preapprovalDetails, DomainSet preVerifiedDomains) {
+            PreapprovalDetails preapprovalDetails, DomainSet preVerifiedDomains,
+            String packageNameFromApk) {
         this.sessionId = sessionId;
         this.userId = userId;
         this.mOriginalInstallerUid = originalInstallerUid;
@@ -135,6 +137,9 @@
         } else {
             this.mPreVerifiedDomains = null;
         }
+
+        this.mPackageName = preapprovalDetails != null ? preapprovalDetails.getPackageName()
+                : packageNameFromApk != null ? packageNameFromApk : params.appPackageName;
     }
 
     void dump(IndentingPrintWriter pw) {
@@ -178,6 +183,7 @@
         pw.printPair("mSessionErrorMessage", mSessionErrorMessage);
         pw.printPair("mPreapprovalDetails", mPreapprovalDetails);
         pw.printPair("mPreVerifiedDomains", mPreVerifiedDomains);
+        pw.printPair("mAppPackageName", mPackageName);
         pw.println();
 
         pw.decreaseIndent();
@@ -206,6 +212,7 @@
         info.createdMillis = mCreatedMillis;
         info.updatedMillis = mUpdatedMillis;
         info.installerUid = mInstallerUid;
+        info.appPackageName = mPackageName;
         return info;
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c860b5a..5792d86 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1242,7 +1242,7 @@
                     mStageDirInUse, mDestroyed, mFds.size(), mBridges.size(), mFinalStatus,
                     mFinalMessage, params, mParentSessionId, getChildSessionIdsLocked(),
                     mSessionApplied, mSessionFailed, mSessionReady, mSessionErrorCode,
-                    mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains);
+                    mSessionErrorMessage, mPreapprovalDetails, mPreVerifiedDomains, mPackageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 76952b3..1b220a0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -17,7 +17,7 @@
 package com.android.server.policy;
 
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -94,7 +94,7 @@
     private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
 
     @VisibleForTesting
-    static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
+    static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER,
             "DEFAULT", 0 /* flags */);
 
     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 5f4852f..a25d67a 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -252,7 +252,7 @@
             }
 
             getInboundTransformInternal()
-                    .getIpSecTransformState(
+                    .requestIpSecTransformState(
                             new HandlerExecutor(mHandler), new IpSecTransformStateReceiver());
 
             // Schedule for next poll
@@ -302,7 +302,8 @@
                 "packetLossRate: "
                         + packetLossRate
                         + "% in the past "
-                        + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp())
+                        + (state.getTimestampMillis()
+                                - mLastIpSecTransformState.getTimestampMillis())
                         + "ms";
 
         mLastIpSecTransformState = state;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index a79f188..1704aa1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -138,10 +138,10 @@
         }
 
         /** Poll an IpSecTransformState */
-        public void getIpSecTransformState(
+        public void requestIpSecTransformState(
                 @NonNull Executor executor,
                 @NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) {
-            ipSecTransform.getIpSecTransformState(executor, callback);
+            ipSecTransform.requestIpSecTransformState(executor, callback);
         }
 
         /** Close this instance and release the underlying resources */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 52fdfda..3637ab1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3524,10 +3524,13 @@
             }
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                hasRestrictedWindow = displayContent.forAllWindows(windowState -> {
-                    return windowState.isOnScreen() && UserManager.isUserTypePrivateProfile(
-                            getUserManager().getProfileType(windowState.mShowUserId));
-                }, true /* traverseTopToBottom */);
+                hasRestrictedWindow = displayContent.forAllWindows(
+                        windowState -> windowState.isOnScreen() && (
+                                UserManager.isUserTypePrivateProfile(
+                                        getUserManager().getProfileType(windowState.mShowUserId))
+                                        || hasUserRestriction(
+                                        UserManager.DISALLOW_ASSIST_CONTENT,
+                                        windowState.mShowUserId)), true /* traverseTopToBottom */);
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 3bc5319..86ca1ea 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -726,9 +726,8 @@
             // Allowed before V by creator
             if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked"
-                                + " if the PI creator upgrades target_sdk to 35+! "
-                                + " (missing opt in by PI creator)! "
-                                + state.dump());
+                        + " if the PI creator upgrades target_sdk to 35+! "
+                        + " (missing opt in by PI creator)!" + state.dump());
                 showBalRiskToast();
                 return allowBasedOnCaller(state);
             }
@@ -737,17 +736,15 @@
             // Allowed before U by sender
             if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) {
                 Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked"
-                                + " if the PI sender upgrades target_sdk to 34+! "
-                                + " (missing opt in by PI sender)! "
-                                + state.dump());
+                        + " if the PI sender upgrades target_sdk to 34+! "
+                        + " (missing opt in by PI sender)!" + state.dump());
                 showBalRiskToast();
                 return allowBasedOnRealCaller(state);
             }
         }
         // caller or real caller could start the activity, but would need to explicitly opt in
         if (callerCanAllow || realCallerCanAllow) {
-            Slog.wtf(TAG, "Without BAL hardening this activity start would be allowed "
-                            + state.dump());
+            Slog.w(TAG, "Without BAL hardening this activity start would be allowed");
         }
         // neither the caller not the realCaller can allow or have explicitly opted out
         return abortLaunch(state);
@@ -770,7 +767,7 @@
     }
 
     private BalVerdict abortLaunch(BalState state) {
-        Slog.w(TAG, "Background activity launch blocked! "
+        Slog.wtf(TAG, "Background activity launch blocked! "
                 + state.dump());
         showBalBlockedToast();
         return statsLog(BalVerdict.BLOCK, state);
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 68bff43..fac62fc 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -178,8 +178,8 @@
         return true;
     }
 
-    boolean transferToHost(InputTransferToken embeddedWindowToken,
-            WindowState transferToHostWindowState) {
+    boolean transferToHost(@NonNull InputTransferToken embeddedWindowToken,
+            @NonNull WindowState transferToHostWindowState) {
         EmbeddedWindow ew = getByInputTransferToken(embeddedWindowToken);
         if (!isValidTouchGestureParams(transferToHostWindowState, ew)) {
             return false;
@@ -188,7 +188,8 @@
                 transferToHostWindowState.mInputChannelToken);
     }
 
-    boolean transferToEmbedded(WindowState hostWindowState, InputTransferToken transferToToken) {
+    boolean transferToEmbedded(WindowState hostWindowState,
+            @NonNull InputTransferToken transferToToken) {
         final EmbeddedWindowController.EmbeddedWindow ew = getByInputTransferToken(transferToToken);
         if (!isValidTouchGestureParams(hostWindowState, ew)) {
             return false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3d6bd4f..e538f5d5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -101,7 +101,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_EMBEDDED_WINDOWS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
@@ -9057,14 +9056,10 @@
     }
 
     @Override
-    public boolean transferTouchGesture(InputTransferToken transferFromToken,
-            InputTransferToken transferToToken) {
-        if (transferFromToken == null || transferToToken == null) {
-            ProtoLog.e(WM_DEBUG_EMBEDDED_WINDOWS,
-                    "transferTouchGesture failed because args transferFromToken or "
-                            + "transferToToken is null");
-            return false;
-        }
+    public boolean transferTouchGesture(@NonNull InputTransferToken transferFromToken,
+            @NonNull InputTransferToken transferToToken) {
+        Objects.requireNonNull(transferFromToken);
+        Objects.requireNonNull(transferToToken);
 
         final long identity = Binder.clearCallingIdentity();
         boolean didTransfer;
diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
index adb1b72..d06d4d8 100644
--- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java
@@ -168,6 +168,9 @@
         mRequestSessionMetric.collectFrameworkException(exception);
         if (finalResponseReceiver != null) {
             Bundle resultData = new Bundle();
+            resultData.putStringArray(
+                    CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION,
+                    new String[] {exception, message});
             finalResponseReceiver.send(Constants.FAILURE_CREDMAN_SELECTOR, resultData);
         } else {
             respondToClientWithErrorAndFinish(exception, message);
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index bf2619b..42e41d5 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -18,8 +18,8 @@
 
 import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST;
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.TYPE_EXTERNAL;
 
@@ -478,7 +478,8 @@
         }
 
         public static DeviceStateConfiguration createConfig(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
                 @NonNull String name,
                 @DeviceState.DeviceStateFlags int flags,
                 @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
@@ -488,7 +489,8 @@
         }
 
         public static DeviceStateConfiguration createConfig(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
                 @NonNull String name,
                 @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
         ) {
@@ -498,7 +500,8 @@
 
         /** Create a configuration with availability predicate **/
         public static DeviceStateConfiguration createConfig(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
                 @NonNull String name,
                 @DeviceState.DeviceStateFlags int flags,
                 @NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate,
@@ -543,7 +546,8 @@
          * @return device state configuration
          */
         public static DeviceStateConfiguration createTentModeClosedState(
-                @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+                @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+                        MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
                 @NonNull String name,
                 @DeviceState.DeviceStateFlags int flags,
                 int minClosedAngleDegrees,
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 cb3ee73..4b086b3 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
@@ -590,8 +590,13 @@
                 var areGidsPerUser = false
                 if (!parsedPermission.isTree && packageState.isSystem) {
                     newState.externalState.configPermissions[permissionName]?.let {
-                        gids = it.gids
-                        areGidsPerUser = it.perUser
+                        // PermissionEntry.gids may return null when parsing legacy config trying
+                        // to work around an issue about upgrading from L platfrm. We can just
+                        // ignore such entries now.
+                        if (it.gids != null) {
+                            gids = it.gids
+                            areGidsPerUser = it.perUser
+                        }
                     }
                 }
                 newPermission = Permission(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index b12d6da..89d146d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -41,12 +41,12 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.TextUtils;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.IWindow;
@@ -452,7 +452,9 @@
                 false, mockHostToken, USER_SYSTEM_ID);
         final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
                 false, mockEmbeddedToken, USER_SYSTEM_ID);
+
         mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+
         final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
                 embeddedWindowId);
         assertEquals(hostWindowId, resolvedWindowId);
@@ -467,10 +469,13 @@
                 false, mockHostToken, USER_SYSTEM_ID);
         final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
                 false, mockEmbeddedToken, USER_SYSTEM_ID);
+
         mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
         mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+
         final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
                 embeddedWindowId);
+        assertNotEquals(hostWindowId, resolvedWindowId);
         assertEquals(embeddedWindowId, resolvedWindowId);
     }
 
@@ -917,7 +922,7 @@
 
         final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked(
                 windowId);
-        assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle()));
+        assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle()));
     }
 
     @Test
@@ -1057,7 +1062,7 @@
         when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
         when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
                 .thenReturn(bGlobal);
-        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
                 .thenReturn(displayId);
 
         int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1077,7 +1082,7 @@
         when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
         when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
                 .thenReturn(bGlobal);
-        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+        when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
                 .thenReturn(displayId);
 
         int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1148,6 +1153,11 @@
         }
     }
 
+    @Nullable
+    private static String toString(@Nullable CharSequence cs) {
+        return cs == null ? null : cs.toString();
+    }
+
     static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
         private final int mDisplayId;
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd904eb..a0c4b5e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -150,6 +150,9 @@
     @Mock
     private DisplayManagerInternal mDisplayManagerInternal;
 
+    @Mock
+    private Scroller mMockScroller;
+
     // To mock package-private class
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -208,7 +211,7 @@
                                 mScaleProvider,
                                 () -> null,
                                 ConcurrentUtils.DIRECT_EXECUTOR,
-                                () -> new Scroller(mContext),
+                                () -> mMockScroller,
                                 () -> mTimeAnimator));
         mScreenMagnificationController.register(TEST_DISPLAY);
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 071db68..f0dc5f0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -60,8 +60,10 @@
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.iris.IIrisService;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -127,6 +129,8 @@
     AppOpsManager mAppOpsManager;
     @Mock
     private VirtualDeviceManagerInternal mVdmInternal;
+    @Mock
+    private BiometricHandlerProvider mBiometricHandlerProvider;
     @Captor
     private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
     @Captor
@@ -136,6 +140,9 @@
     @Captor
     private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor;
 
+    private final TestLooper mFingerprintLooper = new TestLooper();
+    private final TestLooper mFaceLooper = new TestLooper();
+
     @Before
     public void setUp() {
         // Placeholder test config
@@ -167,6 +174,11 @@
         when(mInjector.getIrisService()).thenReturn(mIrisService);
         when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
         when(mInjector.isHidlDisabled(any())).thenReturn(false);
+        when(mInjector.getBiometricHandlerProvider()).thenReturn(mBiometricHandlerProvider);
+        when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+                new Handler(mFingerprintLooper.getLooper()));
+        when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(
+                new Handler(mFaceLooper.getLooper()));
 
         setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */);
     }
@@ -250,6 +262,9 @@
         mAuthService = new AuthService(mContext, mInjector);
         mAuthService.onStart();
 
+        mFingerprintLooper.dispatchAll();
+        mFaceLooper.dispatchAll();
+
         verify(mFingerprintService).registerAuthenticatorsLegacy(
                 mFingerprintSensorConfigurationsCaptor.capture());
 
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 5f2abc3..3492935 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -205,6 +205,7 @@
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -2217,6 +2218,7 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
     }
 
+    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainOnTempAllowlistChange() throws Exception {
@@ -2246,6 +2248,7 @@
         assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
     }
 
+    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersProcStateChanges() throws Exception {
@@ -2308,6 +2311,7 @@
         waitForUidEventHandlerIdle();
     }
 
+    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersStaleChanges() throws Exception {
@@ -2328,6 +2332,7 @@
         waitForUidEventHandlerIdle();
     }
 
+    @Ignore("Temporarily disabled until the feature is enabled")
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testUidObserverFiltersCapabilityChanges() throws Exception {
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 12f9e26..abfb95c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -625,6 +625,21 @@
     }
 
     @Test
+    public void testRuleXml_customInterruptionFilter() throws Exception {
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+        rule.conditionId = Uri.parse("condition://android/blah");
+        assertThat(Condition.isValidId(rule.conditionId, ZenModeConfig.SYSTEM_AUTHORITY)).isTrue();
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeRuleXml(rule, baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+        assertEquals(rule.zenMode, fromXml.zenMode);
+    }
+
+    @Test
     public void testZenPolicyXml_allUnset() throws Exception {
         ZenPolicy policy = new ZenPolicy.Builder().build();
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 697c8ec..d99abe8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3844,13 +3844,27 @@
      * and if the 5G state changes to neither 'connected' not 'not_restricted_rrc_idle', the icon
      * will change to reflect the true state.
      *
+     * The value can be overridden by {@link #KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT}
      * @hide
      */
     public static final String KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING =
             "5g_icon_display_secondary_grace_period_string";
 
     /**
-     * Whether device reset all of NR timers when device camped on a network that haven't 5G
+     * The secondary grace periods in seconds to use if NR advanced icon was shown due to connecting
+     * to bands specified in {@link #KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY}.
+     *
+     * The default value is 0, meaning the original value in
+     * {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING} is used. Otherwise, it overrides
+     * the value in {@link #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING}.
+     *
+     * @hide
+     */
+    public static final String KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT =
+            "nr_advanced_bands_secondary_timer_seconds_int";
+
+    /**
+     * Whether device resets all of NR timers when device camped on a network that haven't 5G
      * capability and RRC currently in IDLE state.
      *
      * The default value is false;
@@ -3861,6 +3875,30 @@
             "nr_timers_reset_if_non_endc_and_rrc_idle_bool";
 
     /**
+     * Whether device resets all of NR timers when device is in a voice call and QOS is established.
+     * The default value is false;
+     *
+     * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+     * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+     *
+     * @hide
+     */
+    public static final String KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL =
+            "nr_timers_reset_on_voice_qos_bool";
+
+    /**
+     * Whether device resets all of NR timers when the PLMN changes.
+     * The default value is false;
+     *
+     * @see #KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING
+     * @see #KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING
+     *
+     * @hide
+     */
+    public static final String KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL =
+            "nr_timers_reset_on_plmn_change_bool";
+
+    /**
      * A list of additional NR advanced band would map to
      * {@link TelephonyDisplayInfo#OVERRIDE_NETWORK_TYPE_NR_ADVANCED} when the device is on that
      * band.
@@ -10688,7 +10726,10 @@
                         + "not_restricted_rrc_con:5G");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING, "");
         sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, "");
+        sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0);
         sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false);
+        sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false);
         /* Default value is 1 hour. */
         sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
         sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 9daba6a..1d7be2f 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -144,7 +144,7 @@
         mTestLooper.dispatchAll();
 
         verify(mIpSecTransform)
-                .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
+                .requestIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
         return mTransformStateReceiverCaptor.getValue();
     }
 
@@ -210,7 +210,7 @@
         assertNull(mIpSecPacketLossDetector.getLastTransformState());
         mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
         mTestLooper.dispatchAll();
-        verify(newTransform).getIpSecTransformState(any(), any());
+        verify(newTransform).requestIpSecTransformState(any(), any());
     }
 
     @Test
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
new file mode 100644
index 0000000..83e09bf
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toJvmClassName
+
+/**
+ * Filter to apply a policy to classes extending or implementing a class,
+ * either directly or indirectly. (with a breadth first search.)
+ *
+ * The policy won't apply to the super class itself.
+ */
+class SubclassFilter(
+        private val classes: ClassNodes,
+        fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+    private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
+
+    /**
+     * Add a policy to all classes extending or implementing a class, either directly or indirectly.
+     */
+    fun addPolicy(superClassName: String, policy: FilterPolicyWithReason) {
+        mPolicies[superClassName.toJvmClassName()] = policy
+    }
+
+    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+        return findPolicyForClass(className) ?: super.getPolicyForClass(className)
+    }
+
+    /**
+     * Find a policy for a class with a breadth-first search.
+     */
+    private fun findPolicyForClass(className: String): FilterPolicyWithReason? {
+        val cn = classes.findClass(className) ?: return null
+
+        if (cn.superName == null) {
+            return null
+        }
+        // First, check the direct super class / interfaces.
+        mPolicies[cn.superName]?.let { policy ->
+            return policy
+        }
+        cn.interfaces?.forEach { iface ->
+            mPolicies[iface]?.let { policy ->
+                return policy
+            }
+        }
+
+        // Then recurse.
+        cn.superName?.let { superName ->
+            findPolicyForClass(superName)?.let { policy ->
+                return policy
+            }
+        }
+        cn.interfaces?.forEach { iface ->
+            findPolicyForClass(iface)?.let { policy ->
+                return policy
+            }
+        }
+        return null
+    }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 6ad83fb..75b5fc8 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -58,7 +58,8 @@
         ): OutputFilter {
     log.i("Loading offloaded annotations from $filename ...")
     log.withIndent {
-        val imf = InMemoryOutputFilter(classes, fallback)
+        val subclassFilter = SubclassFilter(classes, fallback)
+        val imf = InMemoryOutputFilter(classes, subclassFilter)
 
         var lineNo = 0
 
@@ -94,6 +95,10 @@
                             }
                             className = fields[1]
 
+                            // superClass is set when the class name starts with a "*".
+                            val superClass = resolveExtendingClass(className)
+
+                            // :aidl, etc?
                             val classType = resolveSpecialClass(className)
 
                             if (fields[2].startsWith("!")) {
@@ -124,8 +129,14 @@
                                 when (classType) {
                                     SpecialClass.NotSpecial -> {
                                         // TODO: Duplicate check, etc
-                                        imf.setPolicyForClass(
-                                                className, policy.withReason(FILTER_REASON))
+                                        if (superClass == null) {
+                                            imf.setPolicyForClass(
+                                                className, policy.withReason(FILTER_REASON)
+                                            )
+                                        } else {
+                                            subclassFilter.addPolicy(superClass,
+                                                policy.withReason("extends $superClass"))
+                                        }
                                     }
                                     SpecialClass.Aidl -> {
                                         if (aidlPolicy != null) {
@@ -243,6 +254,13 @@
     throw ParseException("Invalid special class name \"$className\"")
 }
 
+private fun resolveExtendingClass(className: String): String? {
+    if (!className.startsWith("*")) {
+        return null
+    }
+    return className.substring(1)
+}
+
 private fun parsePolicy(s: String): FilterPolicy {
     return when (s.lowercase()) {
         "s", "stub" -> FilterPolicy.Stub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 70f56ae..78f277e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -2589,6 +2589,567 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/C1;
+}
+SourceFile: "C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/C2;
+}
+SourceFile: "C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/C3;
+}
+SourceFile: "C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/CA;
+}
+SourceFile: "CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/CB;
+}
+SourceFile: "CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+  Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C1;
+}
+SourceFile: "Class_C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+  Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C2;
+}
+SourceFile: "Class_C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+  Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/C3."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C3;
+}
+SourceFile: "Class_C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.class
+  Compiled from "Class_CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA extends com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/CA."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CA;
+}
+SourceFile: "Class_CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.class
+  Compiled from "Class_CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB extends com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB;
+}
+SourceFile: "Class_CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.class
+  Compiled from "Class_CB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA extends com.android.hoststubgen.test.tinyframework.subclasstest.CB implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA;
+}
+SourceFile: "Class_CB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+  Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1;
+}
+SourceFile: "Class_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+  Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA;
+}
+SourceFile: "Class_I1_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+  Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I2;
+}
+SourceFile: "Class_I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+  Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3;
+}
+SourceFile: "Class_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.class
+  Compiled from "Class_I3_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I3,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA;
+}
+SourceFile: "Class_I3_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.class
+  Compiled from "Class_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA;
+}
+SourceFile: "Class_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.class
+  Compiled from "Class_IA_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1;
+}
+SourceFile: "Class_IA_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.class
+  Compiled from "Class_IA_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3;
+}
+SourceFile: "Class_IA_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.class
+  Compiled from "Class_IB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB implements com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB;
+}
+SourceFile: "Class_IB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.class
+  Compiled from "Class_IB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IB,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA;
+}
+SourceFile: "Class_IB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.class
+  Compiled from "Class_None.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 1
+  public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_None;
+}
+SourceFile: "Class_None.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IB.java"
 ## Class: com/supported/UnsupportedClass.class
   Compiled from "UnsupportedClass.java"
 public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index b0db483..406cb74 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -2055,6 +2055,166 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/unsupported/UnsupportedClass.class
   Compiled from "UnsupportedClass.java"
 public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 112f69e..c673262 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -3371,6 +3371,264 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+  Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+  Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+  Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+  Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+  Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+  Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+  Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/supported/UnsupportedClass.class
   Compiled from "UnsupportedClass.java"
 public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index b0db483..406cb74 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -2055,6 +2055,166 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/unsupported/UnsupportedClass.class
   Compiled from "UnsupportedClass.java"
 public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 2357844..4fd5701 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -4201,6 +4201,417 @@
 RuntimeInvisibleAnnotations:
   x: #x()
     android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+  Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+  Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+  Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+  Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+  Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+  Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+  Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
 ## Class: com/supported/UnsupportedClass.class
   Compiled from "UnsupportedClass.java"
 public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 9b6b6e4..d302084 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -17,4 +17,23 @@
 class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy	~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
 
 # Heuristics rule: Stub all the AIDL classes.
-class :aidl stubclass
\ No newline at end of file
+class :aidl stubclass
+
+# Default is "remove", so let's put all the base classes / interfaces in the stub first.
+class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CB stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IB stub
+
+# Then define inheritance based policies.
+class *com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.CA remove
+
+class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
similarity index 79%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
index 4dd2289..03c9e2a 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class C1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
similarity index 78%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
index 4dd2289..3ca8f1f 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class C2 extends C1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
similarity index 78%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
index 4dd2289..a6c14f0 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class C3 extends C2 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
similarity index 79%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
index 4dd2289..2e35370 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class CA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
similarity index 79%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
index 4dd2289..fe4cee6 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class CB {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
index 4dd2289..12012fc 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_C1 extends C1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
index 4dd2289..8d48ee6 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_C2 extends C2 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
index 4dd2289..6748430 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_C3 extends C3 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
index 4dd2289..58aa5c3 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_CA extends CA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
index 4dd2289..c1c3d62 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_CB extends CB {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
similarity index 75%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
index 4dd2289..398b569 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_CB_IA extends CB implements IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
index 4dd2289..44cbd8f 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_I1 implements I1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
similarity index 76%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
index 4dd2289..42355a3 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_I1_IA implements I1, IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
index 4dd2289..09c8099 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_I2 implements I2 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
index 4dd2289..0806a47 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_I3 implements I3 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
similarity index 76%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
index 4dd2289..eaa8528 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_I3_IA implements I3, IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
index 4dd2289..778c5aa 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_IA implements IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
similarity index 76%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
index 4dd2289..493f7c8 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_IA_I1 implements IA, I1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
similarity index 76%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
index 4dd2289..2aa1de1 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_IA_I3 implements IA, I3 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
index 4dd2289..d9eae09 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_IB implements IB {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
similarity index 76%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
index 4dd2289..9ee4283 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_IB_IA implements IB, IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
similarity index 78%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
index 4dd2289..50ec2cb 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public class Class_None {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
similarity index 79%
rename from core/java/android/service/voice/HotwordTrainingAudio.aidl
rename to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
index 4dd2289..3f36596 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public interface I1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
index 4dd2289..960060c 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public interface I2 extends I1 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
similarity index 77%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
index 4dd2289..c678eaa 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public interface I3 extends I2 {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
similarity index 79%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
index 4dd2289..1cff484 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public interface IA {
+}
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
similarity index 79%
copy from core/java/android/service/voice/HotwordTrainingAudio.aidl
copy to tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
index 4dd2289..84e7173 100644
--- a/core/java/android/service/voice/HotwordTrainingAudio.aidl
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
@@ -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.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.hoststubgen.test.tinyframework.subclasstest;
 
-package android.service.voice;
-
-parcelable HotwordTrainingAudio;
\ No newline at end of file
+public interface IB {
+}