Merge "Dump KeyguardMediaController state" into main
diff --git a/Android.bp b/Android.bp
index 49386d4..895ef98 100644
--- a/Android.bp
+++ b/Android.bp
@@ -399,6 +399,7 @@
         "soundtrigger_middleware-aidl-java",
         "modules-utils-binary-xml",
         "modules-utils-build",
+        "modules-utils-fastxmlserializer",
         "modules-utils-preconditions",
         "modules-utils-statemachine",
         "modules-utils-synchronous-result-receiver",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 1582266..3310898 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -77,10 +77,17 @@
         "framework-minus-apex.ravenwood",
         "hoststubgen-helper-runtime.ravenwood",
         "hoststubgen-helper-framework-runtime.ravenwood",
+        "junit",
+        "truth",
+        "ravenwood-junit",
     ],
 }
 
 android_ravenwood_libgroup {
     name: "ravenwood-utils",
-    libs: [],
+    libs: [
+        "junit",
+        "truth",
+        "ravenwood-junit",
+    ],
 }
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 887f7fe..6c83add 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -30,6 +30,10 @@
         "unsupportedappusage",
     ],
 
+    static_libs: [
+        "modules-utils-fastxmlserializer",
+    ],
+
     // Rename classes shared with the framework
     jarjar_rules: "jarjar-rules.txt",
 
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 7d38377..12f455a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -2539,6 +2539,38 @@
         pw.println("]");
         pw.println();
 
+        pw.println("mActiveAdminApps=[");
+        synchronized (mActiveAdminApps) {
+            final int size = mActiveAdminApps.size();
+            for (int i = 0; i < size; ++i) {
+                final int userId = mActiveAdminApps.keyAt(i);
+                pw.print(" ");
+                pw.print(userId);
+                pw.print(": ");
+                pw.print(mActiveAdminApps.valueAt(i));
+                if (i != size - 1) pw.print(",");
+                pw.println();
+            }
+        }
+        pw.println("]");
+        pw.println();
+
+        pw.println("mAdminProtectedPackages=[");
+        synchronized (mAdminProtectedPackages) {
+            final int size = mAdminProtectedPackages.size();
+            for (int i = 0; i < size; ++i) {
+                final int userId = mAdminProtectedPackages.keyAt(i);
+                pw.print(" ");
+                pw.print(userId);
+                pw.print(": ");
+                pw.print(mAdminProtectedPackages.valueAt(i));
+                if (i != size - 1) pw.print(",");
+                pw.println();
+            }
+        }
+        pw.println("]");
+        pw.println();
+
         mInjector.dump(pw);
     }
 
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index ee9c464..2d23533 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -142,13 +142,22 @@
             case 'p':
                 png = true;
                 break;
-            case 'd':
-                displayIdOpt = DisplayId::fromValue(atoll(optarg));
+            case 'd': {
+                errno = 0;
+                char* end = nullptr;
+                const uint64_t id = strtoull(optarg, &end, 10);
+                if (!end || *end != '\0' || errno == ERANGE) {
+                    fprintf(stderr, "Invalid display ID: Out of range [0, 2^64).\n");
+                    return 1;
+                }
+
+                displayIdOpt = DisplayId::fromValue(id);
                 if (!displayIdOpt) {
-                    fprintf(stderr, "Invalid display ID: %s\n", optarg);
+                    fprintf(stderr, "Invalid display ID: Incorrect encoding.\n");
                     return 1;
                 }
                 break;
+            }
             case '?':
             case 'h':
                 if (ids.size() == 1) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 87f5b3c..14869db 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5315,11 +5315,15 @@
     method public android.net.Uri getConditionId();
     method @Nullable public android.content.ComponentName getConfigurationActivity();
     method public long getCreationTime();
+    method @FlaggedApi("android.app.modes_api") @DrawableRes public int getIconResId();
     method public int getInterruptionFilter();
     method public String getName();
     method public android.content.ComponentName getOwner();
+    method @FlaggedApi("android.app.modes_api") @Nullable public String getTriggerDescription();
+    method @FlaggedApi("android.app.modes_api") public int getType();
     method public android.service.notification.ZenPolicy getZenPolicy();
     method public boolean isEnabled();
+    method @FlaggedApi("android.app.modes_api") public boolean isManualInvocationAllowed();
     method public void setConditionId(android.net.Uri);
     method public void setConfigurationActivity(@Nullable android.content.ComponentName);
     method public void setEnabled(boolean);
@@ -5328,6 +5332,32 @@
     method public void setZenPolicy(android.service.notification.ZenPolicy);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR;
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_BEDTIME = 3; // 0x3
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_DRIVING = 4; // 0x4
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_IMMERSIVE = 5; // 0x5
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_MANAGED = 7; // 0x7
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_OTHER = 0; // 0x0
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_CALENDAR = 2; // 0x2
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_SCHEDULE_TIME = 1; // 0x1
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_THEATER = 6; // 0x6
+    field @FlaggedApi("android.app.modes_api") public static final int TYPE_UNKNOWN = -1; // 0xffffffff
+  }
+
+  @FlaggedApi("android.app.modes_api") public static final class AutomaticZenRule.Builder {
+    ctor public AutomaticZenRule.Builder(@NonNull android.app.AutomaticZenRule);
+    ctor public AutomaticZenRule.Builder(@NonNull String, @NonNull android.net.Uri);
+    method @NonNull public android.app.AutomaticZenRule build();
+    method @NonNull public android.app.AutomaticZenRule.Builder setConditionId(@NonNull android.net.Uri);
+    method @NonNull public android.app.AutomaticZenRule.Builder setConfigurationActivity(@Nullable android.content.ComponentName);
+    method @NonNull public android.app.AutomaticZenRule.Builder setEnabled(boolean);
+    method @NonNull public android.app.AutomaticZenRule.Builder setIconResId(@DrawableRes int);
+    method @NonNull public android.app.AutomaticZenRule.Builder setInterruptionFilter(int);
+    method @NonNull public android.app.AutomaticZenRule.Builder setManualInvocationAllowed(boolean);
+    method @NonNull public android.app.AutomaticZenRule.Builder setName(@NonNull String);
+    method @NonNull public android.app.AutomaticZenRule.Builder setOwner(@Nullable android.content.ComponentName);
+    method @NonNull public android.app.AutomaticZenRule.Builder setTriggerDescription(@Nullable String);
+    method @NonNull public android.app.AutomaticZenRule.Builder setType(int);
+    method @NonNull public android.app.AutomaticZenRule.Builder setZenPolicy(@Nullable android.service.notification.ZenPolicy);
   }
 
   public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 0737496..df466ab 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -144,7 +144,6 @@
   }
 
   public abstract class PackageManager {
-    method @NonNull public String getPermissionControllerPackageName();
     method @NonNull public String getSdkSandboxPackageName();
     method @RequiresPermission(android.Manifest.permission.MAKE_UID_VISIBLE) public void makeUidVisible(int, int);
     field public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.VERIFICATION_ROOT_HASH";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 739fdc5..b5e9407 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3916,6 +3916,7 @@
     method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public String getPermissionControllerPackageName();
     method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
@@ -9733,6 +9734,7 @@
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getUid();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean hasCategory(@NonNull String);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOnHost();
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean isOtherServiceEnabled();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadAppLabel(@NonNull android.content.pm.PackageManager);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadBanner(@NonNull android.content.pm.PackageManager);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.graphics.drawable.Drawable loadIcon(@NonNull android.content.pm.PackageManager);
@@ -9743,6 +9745,7 @@
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setDynamicAidGroup(@NonNull android.nfc.cardemulation.AidGroup);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOffHostSecureElement(@NonNull String);
+    method @FlaggedApi("android.nfc.enable_nfc_mainline") public void setOtherServiceEnabled(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void writeToParcel(@NonNull android.os.Parcel, int);
     field @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.ApduServiceInfo> CREATOR;
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b2bfda1..8b20720 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3592,8 +3592,8 @@
     field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
     field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000
     field public CharSequence accessibilityTitle;
-    field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMaxDisplayRefreshRate;
-    field @FlaggedApi("android.view.flags.wm_display_refresh_rate_test") public float preferredMinDisplayRefreshRate;
+    field public float preferredMaxDisplayRefreshRate;
+    field public float preferredMinDisplayRefreshRate;
     field public int privateFlags;
   }
 
diff --git a/core/java/Android.bp b/core/java/Android.bp
index ddb221f..48cafc5 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -226,7 +226,6 @@
         "com/android/internal/util/ConcurrentUtils.java",
         "com/android/internal/util/DumpUtils.java",
         "com/android/internal/util/FastPrintWriter.java",
-        "com/android/internal/util/FastXmlSerializer.java",
         "com/android/internal/util/FunctionalUtils.java",
         "com/android/internal/util/ParseUtils.java",
         "com/android/internal/util/RingBufferIndices.java",
@@ -465,7 +464,6 @@
         "com/android/internal/util/AsyncChannel.java",
         "com/android/internal/util/AsyncService.java",
         "com/android/internal/util/BitwiseInputStream.java",
-        "com/android/internal/util/FastXmlSerializer.java",
         "com/android/internal/util/HexDump.java",
         "com/android/internal/util/IndentingPrintWriter.java",
         "com/android/internal/util/UserIcons.java",
@@ -515,7 +513,6 @@
         "android/net/InterfaceConfiguration.java",
         "android/util/BackupUtils.java",
         "android/util/Rational.java",
-        "com/android/internal/util/FastXmlSerializer.java",
         "com/android/internal/util/HexDump.java",
         "com/android/internal/util/MessageUtils.java",
         "com/android/internal/util/WakeupMessage.java",
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3b6ea14..c136db6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -378,6 +378,15 @@
     /** Maps from activity token to the pending override configuration. */
     @GuardedBy("mPendingOverrideConfigs")
     private final ArrayMap<IBinder, Configuration> mPendingOverrideConfigs = new ArrayMap<>();
+
+    /**
+     * A queue of pending ApplicationInfo updates. In case when we get a concurrent update
+     * this queue allows us to only apply the latest object, and it can be applied on demand
+     * instead of waiting for the handler thread to reach the scheduled callback.
+     */
+    @GuardedBy("mResourcesManager")
+    private final ArrayMap<String, ApplicationInfo> mPendingAppInfoUpdates = new ArrayMap<>();
+
     /** The activities to be truly destroyed (not include relaunch). */
     final Map<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed =
             Collections.synchronizedMap(new ArrayMap<>());
@@ -1326,9 +1335,19 @@
         }
 
         public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+            synchronized (mResourcesManager) {
+                var oldAi = mPendingAppInfoUpdates.put(ai.packageName, ai);
+                if (oldAi != null && oldAi.createTimestamp > ai.createTimestamp) {
+                    Slog.w(TAG, "Skipping application info changed for obsolete AI with TS "
+                            + ai.createTimestamp + " < already pending TS "
+                            + oldAi.createTimestamp);
+                    mPendingAppInfoUpdates.put(ai.packageName, oldAi);
+                    return;
+                }
+            }
             mResourcesManager.appendPendingAppInfoUpdate(new String[]{ai.sourceDir}, ai);
-            mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai);
-            sendMessage(H.APPLICATION_INFO_CHANGED, ai);
+            mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai.packageName);
+            sendMessage(H.APPLICATION_INFO_CHANGED, ai.packageName);
         }
 
         public void updateTimeZone() {
@@ -2507,7 +2526,7 @@
                     break;
                 }
                 case APPLICATION_INFO_CHANGED:
-                    handleApplicationInfoChanged((ApplicationInfo) msg.obj);
+                    applyPendingApplicationInfoChanges((String) msg.obj);
                     break;
                 case RUN_ISOLATED_ENTRY_POINT:
                     handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1,
@@ -4070,7 +4089,8 @@
             mProfiler.startProfiling();
         }
 
-        // Make sure we are running with the most recent config.
+        // Make sure we are running with the most recent config and resource paths.
+        applyPendingApplicationInfoChanges(r.activityInfo.packageName);
         mConfigurationController.handleConfigurationChanged(null, null);
         updateDeviceIdForNonUIContexts(deviceId);
 
@@ -6438,6 +6458,17 @@
         r.mLastReportedWindowingMode = newWindowingMode;
     }
 
+    private void applyPendingApplicationInfoChanges(String packageName) {
+        final ApplicationInfo ai;
+        synchronized (mResourcesManager) {
+            ai = mPendingAppInfoUpdates.remove(packageName);
+        }
+        if (ai == null) {
+            return;
+        }
+        handleApplicationInfoChanged(ai);
+    }
+
     /**
      * Updates the application info.
      *
@@ -6463,6 +6494,16 @@
             apk = ref != null ? ref.get() : null;
             ref = mResourcePackages.get(ai.packageName);
             resApk = ref != null ? ref.get() : null;
+            for (ActivityClientRecord ar : mActivities.values()) {
+                if (ar.activityInfo.applicationInfo.packageName.equals(ai.packageName)) {
+                    ar.activityInfo.applicationInfo = ai;
+                    if (apk != null || resApk != null) {
+                        ar.packageInfo = apk != null ? apk : resApk;
+                    } else {
+                        apk = ar.packageInfo;
+                    }
+                }
+            }
         }
 
         if (apk != null) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 7bfb1b5..919e084 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -16,16 +16,23 @@
 
 package android.app;
 
+import android.annotation.DrawableRes;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.NotificationManager.InterruptionFilter;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.notification.Condition;
 import android.service.notification.ZenPolicy;
+import android.view.WindowInsetsController;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -36,7 +43,67 @@
     private static final int ENABLED = 1;
     /* @hide */
     private static final int DISABLED = 0;
-    private boolean enabled = false;
+
+    /**
+     * Rule is of an unknown type. This is the default value if not provided by the owning app.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_UNKNOWN = -1;
+    /**
+     * Rule is of a known type, but not one of the specific types.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_OTHER = 0;
+    /**
+     * The type for rules triggered according to a time-based schedule.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_SCHEDULE_TIME = 1;
+    /**
+     * The type for rules triggered by calendar events.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_SCHEDULE_CALENDAR = 2;
+    /**
+     * The type for rules triggered by bedtime/sleeping, like time of day, or snore detection.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_BEDTIME = 3;
+    /**
+     * The type for rules triggered by driving detection, like Bluetooth connections or vehicle
+     * sounds.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_DRIVING = 4;
+    /**
+     * The type for rules triggered by the user entering an immersive activity, like opening an app
+     * using {@link WindowInsetsController#hide(int)}.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_IMMERSIVE = 5;
+    /**
+     * The type for rules that have a {@link ZenPolicy} that implies that the
+     * device should not make sound and potentially hide some visual effects; may be triggered
+     * when entering a location where silence is requested, like a theater.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_THEATER = 6;
+    /**
+     * The type for rules created and managed by a device owner. These rules may not be fully
+     * editable by the device user.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final int TYPE_MANAGED = 7;
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_UNKNOWN, TYPE_OTHER, TYPE_SCHEDULE_TIME, TYPE_SCHEDULE_CALENDAR, TYPE_BEDTIME,
+            TYPE_DRIVING, TYPE_IMMERSIVE, TYPE_THEATER, TYPE_MANAGED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    private boolean enabled;
     private String name;
     private @InterruptionFilter int interruptionFilter;
     private Uri conditionId;
@@ -46,6 +113,10 @@
     private ZenPolicy mZenPolicy;
     private boolean mModified = false;
     private String mPkg;
+    private int mType = TYPE_UNKNOWN;
+    private int mIconResId;
+    private String mTriggerDescription;
+    private boolean mAllowManualInvocation;
 
     /**
      * The maximum string length for any string contained in this automatic zen rule. This pertains
@@ -55,6 +126,12 @@
     public static final int MAX_STRING_LENGTH = 1000;
 
     /**
+     * The maximum string length for the trigger description rule, given UI constraints.
+     * @hide
+     */
+    public static final int MAX_DESC_LENGTH = 150;
+
+    /**
      * Creates an automatic zen rule.
      *
      * @param name The name of the rule.
@@ -97,6 +174,7 @@
      *               action ({@link Condition#STATE_TRUE}).
      * @param enabled Whether the rule is enabled.
      */
+    // TODO (b/309088420): deprecate this constructor in favor of the builder
     public AutomaticZenRule(@NonNull String name, @Nullable ComponentName owner,
             @Nullable ComponentName configurationActivity, @NonNull Uri conditionId,
             @Nullable ZenPolicy policy, int interruptionFilter, boolean enabled) {
@@ -134,6 +212,12 @@
         mZenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
         mModified = source.readInt() == ENABLED;
         mPkg = source.readString();
+        if (Flags.modesApi()) {
+            mAllowManualInvocation = source.readBoolean();
+            mIconResId = source.readInt();
+            mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
+            mType = source.readInt();
+        }
     }
 
     /**
@@ -269,6 +353,81 @@
         return mPkg;
     }
 
+    /**
+     * Gets the type of the rule.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Sets the type of the rule.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void setType(@Type int type) {
+        mType = type;
+    }
+
+    /**
+     * Gets the user visible description of when this rule is active
+     * (see {@link Condition#STATE_TRUE}).
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public @Nullable String getTriggerDescription() {
+        return mTriggerDescription;
+    }
+
+    /**
+     * Sets a user visible description of when this rule will be active
+     * (see {@link Condition#STATE_TRUE}).
+     *
+     * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+     * "When connected to [Car Name]".
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void setTriggerDescription(@Nullable String triggerDescription) {
+        mTriggerDescription = triggerDescription;
+    }
+
+    /**
+     * Gets the resource id of the drawable icon for this rule.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public @DrawableRes int getIconResId() {
+        return mIconResId;
+    }
+
+    /**
+     * Sets a resource id of a tintable vector drawable representing the rule in image form.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void setIconResId(int iconResId) {
+        mIconResId = iconResId;
+    }
+
+    /**
+     * Gets whether this rule can be manually activated by the user even when the triggering
+     * condition for the rule is not met.
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public boolean isManualInvocationAllowed() {
+        return mAllowManualInvocation;
+    }
+
+    /**
+     * Sets whether this rule can be manually activated by the user even when the triggering
+     * condition for the rule is not met.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public void setManualInvocationAllowed(boolean allowManualInvocation) {
+        mAllowManualInvocation = allowManualInvocation;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -291,11 +450,17 @@
         dest.writeParcelable(mZenPolicy, 0);
         dest.writeInt(mModified ? ENABLED : DISABLED);
         dest.writeString(mPkg);
+        if (Flags.modesApi()) {
+            dest.writeBoolean(mAllowManualInvocation);
+            dest.writeInt(mIconResId);
+            dest.writeString(mTriggerDescription);
+            dest.writeInt(mType);
+        }
     }
 
     @Override
     public String toString() {
-        return new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
+        StringBuilder sb = new StringBuilder(AutomaticZenRule.class.getSimpleName()).append('[')
                 .append("enabled=").append(enabled)
                 .append(",name=").append(name)
                 .append(",interruptionFilter=").append(interruptionFilter)
@@ -304,8 +469,16 @@
                 .append(",owner=").append(owner)
                 .append(",configActivity=").append(configurationActivity)
                 .append(",creationTime=").append(creationTime)
-                .append(",mZenPolicy=").append(mZenPolicy)
-                .append(']').toString();
+                .append(",mZenPolicy=").append(mZenPolicy);
+
+        if (Flags.modesApi()) {
+            sb.append(",allowManualInvocation=").append(mAllowManualInvocation)
+                    .append(",iconResId=").append(mIconResId)
+                    .append(",triggerDescription=").append(mTriggerDescription)
+                    .append(",type=").append(mType);
+        }
+
+        return sb.append(']').toString();
     }
 
     @Override
@@ -313,7 +486,7 @@
         if (!(o instanceof AutomaticZenRule)) return false;
         if (o == this) return true;
         final AutomaticZenRule other = (AutomaticZenRule) o;
-        return other.enabled == enabled
+        boolean finalEquals = other.enabled == enabled
                 && other.mModified == mModified
                 && Objects.equals(other.name, name)
                 && other.interruptionFilter == interruptionFilter
@@ -323,10 +496,23 @@
                 && Objects.equals(other.configurationActivity, configurationActivity)
                 && Objects.equals(other.mPkg, mPkg)
                 && other.creationTime == creationTime;
+        if (Flags.modesApi()) {
+            return finalEquals
+                    && other.mAllowManualInvocation == mAllowManualInvocation
+                    && other.mIconResId == mIconResId
+                    && Objects.equals(other.mTriggerDescription, mTriggerDescription)
+                    && other.mType == mType;
+        }
+        return finalEquals;
     }
 
     @Override
     public int hashCode() {
+        if (Flags.modesApi()) {
+            return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
+                    configurationActivity, mZenPolicy, mModified, creationTime, mPkg,
+                    mAllowManualInvocation, mIconResId, mTriggerDescription, mType);
+        }
         return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
                 configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
     }
@@ -357,8 +543,12 @@
      * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
      */
     private static String getTrimmedString(String input) {
-        if (input != null && input.length() > MAX_STRING_LENGTH) {
-            return input.substring(0, MAX_STRING_LENGTH);
+        return getTrimmedString(input, MAX_STRING_LENGTH);
+    }
+
+    private static String getTrimmedString(String input, int length) {
+        if (input != null && input.length() > length) {
+            return input.substring(0, length);
         }
         return input;
     }
@@ -373,4 +563,138 @@
         }
         return input;
     }
+
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static final class Builder {
+        private String mName;
+        private ComponentName mOwner;
+        private Uri mConditionId;
+        private int mInterruptionFilter;
+        private boolean mEnabled;
+        private ComponentName mConfigurationActivity = null;
+        private ZenPolicy mPolicy = null;
+        private int mType;
+        private String mDescription;
+        private int mIconResId;
+        private boolean mAllowManualInvocation;
+        private long mCreationTime;
+        private String mPkg;
+
+        public Builder(@NonNull AutomaticZenRule rule) {
+            mName = rule.getName();
+            mOwner = rule.getOwner();
+            mConditionId = rule.getConditionId();
+            mInterruptionFilter = rule.getInterruptionFilter();
+            mEnabled = rule.isEnabled();
+            mConfigurationActivity = rule.getConfigurationActivity();
+            mPolicy = rule.getZenPolicy();
+            mType = rule.getType();
+            mDescription = rule.getTriggerDescription();
+            mIconResId = rule.getIconResId();
+            mAllowManualInvocation = rule.isManualInvocationAllowed();
+            mCreationTime = rule.getCreationTime();
+            mPkg = rule.getPackageName();
+        }
+
+        public Builder(@NonNull String name, @NonNull Uri conditionId) {
+            mName = name;
+            mConditionId = conditionId;
+        }
+
+        public @NonNull Builder setName(@NonNull String name) {
+            mName = name;
+            return this;
+        }
+
+        public @NonNull Builder setOwner(@Nullable ComponentName owner) {
+            mOwner = owner;
+            return this;
+        }
+
+        public @NonNull Builder setConditionId(@NonNull Uri conditionId) {
+            mConditionId = conditionId;
+            return this;
+        }
+
+        public @NonNull Builder setInterruptionFilter(
+                @InterruptionFilter int interruptionFilter) {
+            mInterruptionFilter = interruptionFilter;
+            return this;
+        }
+
+        public @NonNull Builder setEnabled(boolean enabled) {
+            mEnabled = enabled;
+            return this;
+        }
+
+        public @NonNull Builder setConfigurationActivity(
+                @Nullable ComponentName configurationActivity) {
+            mConfigurationActivity = configurationActivity;
+            return this;
+        }
+
+        public @NonNull Builder setZenPolicy(@Nullable ZenPolicy policy) {
+            mPolicy = policy;
+            return this;
+        }
+
+        /**
+         * Sets the type of the rule
+         */
+        public @NonNull Builder setType(@Type int type) {
+            mType = type;
+            return this;
+        }
+
+        /**
+         * Sets a user visible description of when this rule will be active
+         * (see {@link Condition#STATE_TRUE}).
+         *
+         * A description should be a (localized) string like "Mon-Fri, 9pm-7am" or
+         * "When connected to [Car Name]".
+         */
+        public @NonNull Builder setTriggerDescription(@Nullable String description) {
+            mDescription = description;
+            return this;
+        }
+
+        /**
+         * Sets a resource id of a tintable vector drawable representing the rule in image form.
+         */
+        public @NonNull Builder setIconResId(@DrawableRes int iconResId) {
+            mIconResId = iconResId;
+            return this;
+        }
+
+        /**
+         * Sets whether this rule can be manually activated by the user even when the triggering
+         * condition for the rule is not met.
+         */
+        public @NonNull Builder setManualInvocationAllowed(boolean allowManualInvocation) {
+            mAllowManualInvocation = allowManualInvocation;
+            return this;
+        }
+
+        /**
+         * Sets the time at which this rule was created, in milliseconds since epoch
+         * @hide
+         */
+        public @NonNull Builder setCreationTime(long creationTime) {
+            mCreationTime = creationTime;
+            return this;
+        }
+
+        public @NonNull AutomaticZenRule build() {
+            AutomaticZenRule rule = new AutomaticZenRule(mName, mOwner, mConfigurationActivity,
+                    mConditionId, mPolicy, mInterruptionFilter, mEnabled);
+            rule.creationTime = mCreationTime;
+            rule.mType = mType;
+            rule.mTriggerDescription = mDescription;
+            rule.mIconResId = mIconResId;
+            rule.mAllowManualInvocation = mAllowManualInvocation;
+            rule.setPackageName(mPkg);
+
+            return rule;
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 1f8784b..df6badc 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -269,7 +269,10 @@
      * task snapshot cache only if requested.
      *
      * @param taskId the id of the task to take a snapshot of
-     * @param updateCache whether to store the new snapshot in the system's task snapshot cache
+     * @param updateCache Whether to store the new snapshot in the system's task snapshot cache.
+     *                    If it is true, the snapshot can be either real content or app-theme mode
+     *                    depending on the attributes of app. Otherwise, the snapshot will be taken
+     *                    with real content.
      * @return a graphic buffer representing a screenshot of a task
      */
     android.window.TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ebf183f..f1e44cc 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -342,7 +342,9 @@
      */
     public void updateApplicationInfo(@NonNull ApplicationInfo aInfo,
             @Nullable List<String> oldPaths) {
-        setApplicationInfo(aInfo);
+        if (!setApplicationInfo(aInfo)) {
+            return;
+        }
 
         final List<String> newPaths = new ArrayList<>();
         makePaths(mActivityThread, aInfo, newPaths);
@@ -387,7 +389,13 @@
         mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader);
     }
 
-    private void setApplicationInfo(ApplicationInfo aInfo) {
+    private boolean setApplicationInfo(ApplicationInfo aInfo) {
+        if (mApplicationInfo != null && mApplicationInfo.createTimestamp > aInfo.createTimestamp) {
+            Slog.w(TAG, "New application info for package " + aInfo.packageName
+                    + " is out of date with TS " + aInfo.createTimestamp + " < the current TS "
+                    + mApplicationInfo.createTimestamp);
+            return false;
+        }
         final int myUid = Process.myUid();
         aInfo = adjustNativeLibraryPaths(aInfo);
         mApplicationInfo = aInfo;
@@ -410,6 +418,7 @@
         if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
             mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
         }
+        return true;
     }
 
     void setSdkSandboxStorage(@Nullable String sdkSandboxClientAppVolumeUuid,
diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING
index 7809048..49a4467 100644
--- a/core/java/android/app/time/TEST_MAPPING
+++ b/core/java/android/app/time/TEST_MAPPING
@@ -1,24 +1,11 @@
 {
-  "presubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.time."
-        }
-      ]
-    }
-  ],
   // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
+      "name": "FrameworksTimeCoreTests",
       "options": [
         {
-          "include-filter": "android.app.timedetector."
-        },
-        {
-          "include-filter": "android.app.timezonedetector."
+          "include-filter": "android.app."
         }
       ]
     },
diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING
index 53fd74b..c050a55 100644
--- a/core/java/android/app/timedetector/TEST_MAPPING
+++ b/core/java/android/app/timedetector/TEST_MAPPING
@@ -2,13 +2,10 @@
   // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
+      "name": "FrameworksTimeCoreTests",
       "options": [
         {
-          "include-filter": "android.app.time."
-        },
-        {
-          "include-filter": "android.app.timedetector."
+          "include-filter": "android.app."
         }
       ]
     },
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
index 5e64c83..46656d1 100644
--- a/core/java/android/app/timezonedetector/TEST_MAPPING
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -1,21 +1,11 @@
 {
-  "presubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.timezonedetector."
-        }
-      ]
-    }
-  ],
   // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
+      "name": "FrameworksTimeCoreTests",
       "options": [
         {
-          "include-filter": "android.app.time."
+          "include-filter": "android.app."
         }
       ]
     },
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 27f6a26..ec181da 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -156,6 +156,22 @@
     }
 
     /**
+     * @hide
+     */
+    public static class AdapterChildHostView extends AppWidgetHostView {
+
+        public AdapterChildHostView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Context getRemoteContextEnsuringCorrectCachedApkPath() {
+            // To reduce noise in error messages
+            return null;
+        }
+    }
+
+    /**
      * Set the AppWidget that will be displayed by this view. This method also adds default padding
      * to widgets, as described in {@link #getDefaultPaddingForWidget(Context, ComponentName, Rect)}
      * and can be overridden in order to add custom padding.
@@ -921,17 +937,31 @@
         setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
     }
 
+    private void setColorResourcesStates(RemoteViews.ColorResources colorResources) {
+        mColorResources = colorResources;
+        mColorMappingChanged = true;
+        mViewMode = VIEW_MODE_NOINIT;
+    }
+
     /** @hide **/
     public void setColorResources(RemoteViews.ColorResources colorResources) {
         if (colorResources == mColorResources) {
             return;
         }
-        mColorResources = colorResources;
-        mColorMappingChanged = true;
-        mViewMode = VIEW_MODE_NOINIT;
+        setColorResourcesStates(colorResources);
         reapplyLastRemoteViews();
     }
 
+    /**
+     * @hide
+     */
+    public void setColorResourcesNoReapply(RemoteViews.ColorResources colorResources) {
+        if (colorResources == mColorResources) {
+            return;
+        }
+        setColorResourcesStates(colorResources);
+    }
+
     /** Check if, in the current context, the two color mappings are equivalent. */
     private boolean isSameColorMapping(SparseIntArray oldColors, SparseIntArray newColors) {
         if (oldColors.size() != newColors.size()) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e2a5747..72e1066 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6279,9 +6279,9 @@
      * @hide
      */
     @NonNull
-    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SystemApi
     @TestApi
-    @UnsupportedAppUsage
+    @SuppressLint("UnflaggedApi") // Promoting from @SystemApi(MODULE_LIBRARIES)
     public String getPermissionControllerPackageName() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index dbb3127..19ba6a1 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -23,6 +23,7 @@
 import android.os.IBinder;
 import android.os.ServiceManager;
 
+import com.android.net.flags.Flags;
 import com.android.net.module.util.PermissionUtils;
 /**
  * Constants and utilities for client code communicating with the network stack service.
@@ -103,4 +104,16 @@
             final @NonNull String... otherPermissions) {
         PermissionUtils.enforceNetworkStackPermissionOr(context, otherPermissions);
     }
+
+    /**
+     * Get setting of the "set_data_saver_via_cm" flag.
+     *
+     * @hide
+     */
+    // A workaround for aconfig. Currently, aconfig value read from platform and mainline code can
+    // be inconsistent. To avoid the problem, CTS for mainline code can get the flag value by this
+    // method.
+    public static boolean getDataSaverViaCmFlag() {
+        return Flags.setDataSaverViaCm();
+    }
 }
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 9cf8c4d..597c948 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -129,7 +129,7 @@
     /**
      * State of the service for CATEGORY_OTHER selection
      */
-    private boolean mOtherServiceSelectionState;
+    private boolean mOtherServiceEnabled;
 
     /**
      * @hide
@@ -150,10 +150,10 @@
             List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
             String settingsActivityName, String offHost, String staticOffHost,
-            boolean isSelected) {
+            boolean isEnabled) {
         this(info, onHost, description, staticAidGroups, dynamicAidGroups,
                 requiresUnlock, onHost ? true : false, bannerResource, uid,
-                settingsActivityName, offHost, staticOffHost, isSelected);
+                settingsActivityName, offHost, staticOffHost, isEnabled);
     }
 
     /**
@@ -162,7 +162,7 @@
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
             List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
-            String settingsActivityName, String offHost, String staticOffHost, boolean isSelected) {
+            String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -181,7 +181,7 @@
         this.mBannerResourceId = bannerResource;
         this.mUid = uid;
         this.mSettingsActivityName = settingsActivityName;
-        this.mOtherServiceSelectionState = isSelected;
+        this.mOtherServiceEnabled = isEnabled;
 
     }
 
@@ -372,7 +372,7 @@
         // Set uid
         mUid = si.applicationInfo.uid;
 
-        mOtherServiceSelectionState = false;    // support other category
+        mOtherServiceEnabled = false;    // support other category
 
     }
 
@@ -744,7 +744,7 @@
         dest.writeInt(mUid);
         dest.writeString(mSettingsActivityName);
 
-        dest.writeInt(mOtherServiceSelectionState ? 1 : 0);
+        dest.writeInt(mOtherServiceEnabled ? 1 : 0);
     };
 
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -772,11 +772,11 @@
                     int bannerResource = source.readInt();
                     int uid = source.readInt();
                     String settingsActivityName = source.readString();
-                    boolean isSelected = source.readInt() != 0;
+                    boolean isEnabled = source.readInt() != 0;
                     return new ApduServiceInfo(info, onHost, description, staticAidGroups,
                             dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
                             settingsActivityName, offHostName, staticOffHostName,
-                            isSelected);
+                            isEnabled);
                 }
 
                 @Override
@@ -807,7 +807,7 @@
         pw.println("    Static AID groups:");
         for (AidGroup group : mStaticAidGroups.values()) {
             pw.println("        Category: " + group.getCategory()
-                    + "(selected: " + mOtherServiceSelectionState + ")");
+                    + "(enabled: " + mOtherServiceEnabled + ")");
             for (String aid : group.getAids()) {
                 pw.println("            AID: " + aid);
             }
@@ -815,7 +815,7 @@
         pw.println("    Dynamic AID groups:");
         for (AidGroup group : mDynamicAidGroups.values()) {
             pw.println("        Category: " + group.getCategory()
-                    + "(selected: " + mOtherServiceSelectionState + ")");
+                    + "(enabled: " + mOtherServiceEnabled + ")");
             for (String aid : group.getAids()) {
                 pw.println("            AID: " + aid);
             }
@@ -827,18 +827,24 @@
 
 
     /**
-     * @hide
+     * Enable or disable this CATEGORY_OTHER service.
+     *
+     * @param enabled true to indicate if user has enabled this service
      */
-    public void setOtherServiceState(boolean selected) {
-        mOtherServiceSelectionState = selected;
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public void setOtherServiceEnabled(boolean enabled) {
+        mOtherServiceEnabled = enabled;
     }
 
 
     /**
-     * @hide
+     * Returns whether this CATEGORY_OTHER service is enabled or not.
+     *
+     * @return true to indicate if user has enabled this service
      */
-    public boolean isSelectedOtherService() {
-        return mOtherServiceSelectionState;
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+    public boolean isOtherServiceEnabled() {
+        return mOtherServiceEnabled;
     }
 
     /**
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 3db1cb0..9956220 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -333,6 +333,12 @@
     @CriticalNative
     public static final native boolean isDirectlyHandlingTransactionNative();
 
+    /** @hide */
+    public static final boolean isDirectlyHandlingTransactionNative$ravenwood() {
+        // Ravenwood doesn't support IPC
+        return false;
+    }
+
     private static boolean sIsHandlingBinderTransaction = false;
 
     /**
@@ -715,7 +721,9 @@
      */
     public Binder(@Nullable String descriptor) {
         mObject = getNativeBBinderHolder();
-        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
+        if (mObject != 0L) {
+            NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
+        }
 
         if (FIND_POTENTIAL_LEAKS) {
             final Class<? extends Binder> klass = getClass();
@@ -1277,6 +1285,10 @@
 
     private static native long getNativeBBinderHolder();
 
+    private static long getNativeBBinderHolder$ravenwood() {
+        return 0L;
+    }
+
     /**
      * By default, we use the calling UID since we can always trust it.
      */
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 352c9d2..86628d9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1558,7 +1558,7 @@
         ensureWithinMemoryLimit(typeSize, totalObjects);
     }
 
-    private void ensureWithinMemoryLimit(int typeSize, @NonNull int length) {
+    private void ensureWithinMemoryLimit(int typeSize, int length) {
         int estimatedAllocationSize = 0;
         try {
             estimatedAllocationSize = Math.multiplyExact(typeSize, length);
@@ -2957,6 +2957,14 @@
     }
 
     /** @hide */
+    public final void writeException$ravenwood(@NonNull Exception e) {
+        // Ravenwood doesn't support IPC, no transaction headers needed
+        writeInt(getExceptionCode(e));
+        writeString(e.getMessage());
+        writeInt(0);
+    }
+
+    /** @hide */
     public static int getExceptionCode(@NonNull Throwable e) {
         int code = 0;
         if (e instanceof Parcelable
@@ -3039,6 +3047,12 @@
         }
     }
 
+    /** @hide */
+    public final void writeNoException$ravenwood() {
+        // Ravenwood doesn't support IPC, no transaction headers needed
+        writeInt(0);
+    }
+
     /**
      * Special function for reading an exception result from the header of
      * a parcel, to be used after receiving the result of a transaction.  This
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 47b6d8d..94f90cc 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2018,9 +2018,13 @@
             return;
         }
 
+        // Temporarily disable checks so that explicit GC is allowed.
+        final int oldMask = getThreadPolicyMask();
+        setThreadPolicyMask(0);
         System.gc();
         System.runFinalization();
         System.gc();
+        setThreadPolicyMask(oldMask);
 
         // Note: classInstanceLimit is immutable, so this is lock-free
         // Create the classes array.
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 27ad45d..bcda25a 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3206,6 +3206,15 @@
         public static final String INFRASTRUCTURE_BITMASK = "infrastructure_bitmask";
 
         /**
+         * Indicating if the APN is used for eSIM bootsrap provisioning. The default value is 0 (Not
+         * used for eSIM bootstrap provisioning).
+         *
+         * <P>Type: INTEGER</P>
+         * @hide
+         */
+        public static final String ESIM_BOOTSTRAP_PROVISIONING = "esim_bootstrap_provisioning";
+
+        /**
          * MVNO type:
          * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
          * <P>Type: TEXT</P>
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 0133bd8..9bdd0c2 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -36,3 +36,10 @@
     description: "Collect sepolicy hash from sysfs"
     bug: "308471499"
 }
+
+flag {
+    name: "extend_ecm_to_all_settings"
+    namespace: "responsible_apis"
+    description: "Allow all app settings to be restrictable via configuration"
+    bug: "297372999"
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 828c062..ff4dfc7 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -28,6 +28,8 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.AutomaticZenRule;
+import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -177,6 +179,10 @@
     private static final String RULE_ATT_CREATION_TIME = "creationTime";
     private static final String RULE_ATT_ENABLER = "enabler";
     private static final String RULE_ATT_MODIFIED = "modified";
+    private static final String RULE_ATT_ALLOW_MANUAL = "userInvokable";
+    private static final String RULE_ATT_TYPE = "type";
+    private static final String RULE_ATT_ICON = "rule_icon";
+    private static final String RULE_ATT_TRIGGER_DESC = "triggerDesc";
 
     @UnsupportedAppUsage
     public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
@@ -212,7 +218,7 @@
         allowCallsFrom = source.readInt();
         allowMessagesFrom = source.readInt();
         user = source.readInt();
-        manualRule = source.readParcelable(null, android.service.notification.ZenModeConfig.ZenRule.class);
+        manualRule = source.readParcelable(null, ZenRule.class);
         final int len = source.readInt();
         if (len > 0) {
             final String[] ids = new String[len];
@@ -622,6 +628,12 @@
         }
         rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
         rt.zenPolicy = readZenPolicyXml(parser);
+        if (Flags.modesApi()) {
+            rt.allowManualInvocation = safeBoolean(parser, RULE_ATT_ALLOW_MANUAL, false);
+            rt.iconResId = safeInt(parser, RULE_ATT_ICON, 0);
+            rt.triggerDescription = parser.getAttributeValue(null, RULE_ATT_TRIGGER_DESC);
+            rt.type = safeInt(parser, RULE_ATT_TYPE, AutomaticZenRule.TYPE_UNKNOWN);
+        }
         return rt;
     }
 
@@ -655,6 +667,14 @@
             writeZenPolicyXml(rule.zenPolicy, out);
         }
         out.attributeBoolean(null, RULE_ATT_MODIFIED, rule.modified);
+        if (Flags.modesApi()) {
+            out.attributeBoolean(null, RULE_ATT_ALLOW_MANUAL, rule.allowManualInvocation);
+            out.attributeInt(null, RULE_ATT_ICON, rule.iconResId);
+            if (rule.triggerDescription != null) {
+                out.attribute(null, RULE_ATT_TRIGGER_DESC, rule.triggerDescription);
+            }
+            out.attributeInt(null, RULE_ATT_TYPE, rule.type);
+        }
     }
 
     public static Condition readConditionXml(TypedXmlPullParser parser) {
@@ -1726,6 +1746,11 @@
         public ZenPolicy zenPolicy;
         public boolean modified;    // rule has been modified from initial creation
         public String pkg;
+        public int type = AutomaticZenRule.TYPE_UNKNOWN;
+        public String triggerDescription;
+        // TODO (b/308672670): switch to string res name
+        public int iconResId;
+        public boolean allowManualInvocation;
 
         public ZenRule() { }
 
@@ -1750,6 +1775,12 @@
             zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
             modified = source.readInt() == 1;
             pkg = source.readString();
+            if (Flags.modesApi()) {
+                allowManualInvocation = source.readBoolean();
+                iconResId = source.readInt();
+                triggerDescription = source.readString();
+                type = source.readInt();
+            }
         }
 
         @Override
@@ -1788,11 +1819,17 @@
             dest.writeParcelable(zenPolicy, 0);
             dest.writeInt(modified ? 1 : 0);
             dest.writeString(pkg);
+            if (Flags.modesApi()) {
+                dest.writeBoolean(allowManualInvocation);
+                dest.writeInt(iconResId);
+                dest.writeString(triggerDescription);
+                dest.writeInt(type);
+            }
         }
 
         @Override
         public String toString() {
-            return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
+            StringBuilder sb = new StringBuilder(ZenRule.class.getSimpleName()).append('[')
                     .append("id=").append(id)
                     .append(",state=").append(condition == null ? "STATE_FALSE"
                             : Condition.stateToString(condition.state))
@@ -1808,8 +1845,16 @@
                     .append(",enabler=").append(enabler)
                     .append(",zenPolicy=").append(zenPolicy)
                     .append(",modified=").append(modified)
-                    .append(",condition=").append(condition)
-                    .append(']').toString();
+                    .append(",condition=").append(condition);
+
+            if (Flags.modesApi()) {
+                sb.append(",allowManualInvocation=").append(allowManualInvocation)
+                        .append(",iconResId=").append(iconResId)
+                        .append(",triggerDescription=").append(triggerDescription)
+                        .append(",type=").append(type);
+            }
+
+            return sb.append(']').toString();
         }
 
         /** @hide */
@@ -1845,7 +1890,7 @@
             if (!(o instanceof ZenRule)) return false;
             if (o == this) return true;
             final ZenRule other = (ZenRule) o;
-            return other.enabled == enabled
+            boolean finalEquals = other.enabled == enabled
                     && other.snoozing == snoozing
                     && Objects.equals(other.name, name)
                     && other.zenMode == zenMode
@@ -1858,10 +1903,25 @@
                     && Objects.equals(other.zenPolicy, zenPolicy)
                     && Objects.equals(other.pkg, pkg)
                     && other.modified == modified;
+
+            if (Flags.modesApi()) {
+                return finalEquals
+                        && other.allowManualInvocation == allowManualInvocation
+                        && other.iconResId == iconResId
+                        && Objects.equals(other.triggerDescription, triggerDescription)
+                        && other.type == type;
+            }
+
+            return finalEquals;
         }
 
         @Override
         public int hashCode() {
+            if (Flags.modesApi()) {
+                return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
+                        component, configurationActivity, pkg, id, enabler, zenPolicy, modified,
+                        allowManualInvocation, iconResId, triggerDescription, type);
+            }
             return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                     component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
         }
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a4f129e..eb55e40 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -454,6 +454,11 @@
         public static final String FIELD_ZEN_POLICY = "zenPolicy";
         public static final String FIELD_MODIFIED = "modified";
         public static final String FIELD_PKG = "pkg";
+        public static final String FIELD_ALLOW_MANUAL = "allowManualInvocation";
+        public static final String FIELD_ICON_RES = "iconResId";
+        public static final String FIELD_TRIGGER = "triggerDescription";
+        public static final String FIELD_TYPE = "type";
+        // NOTE: new field strings must match the variable names in ZenModeConfig.ZenRule
 
         // Special field to track whether this rule became active or inactive
         FieldDiff<Boolean> mActiveDiff;
@@ -529,6 +534,20 @@
             if (!Objects.equals(from.pkg, to.pkg)) {
                 addField(FIELD_PKG, new FieldDiff<>(from.pkg, to.pkg));
             }
+            if (!Objects.equals(from.triggerDescription, to.triggerDescription)) {
+                addField(FIELD_TRIGGER,
+                        new FieldDiff<>(from.triggerDescription, to.triggerDescription));
+            }
+            if (from.type != to.type) {
+                addField(FIELD_TYPE, new FieldDiff<>(from.type, to.type));
+            }
+            if (from.allowManualInvocation != to.allowManualInvocation) {
+                addField(FIELD_ALLOW_MANUAL,
+                        new FieldDiff<>(from.allowManualInvocation, to.allowManualInvocation));
+            }
+            if (!Objects.equals(from.iconResId, to.iconResId)) {
+                addField(FIELD_ICON_RES, new FieldDiff(from.iconResId, to.iconResId));
+            }
         }
 
         /**
diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING
index b0ce1db..21a8eab 100644
--- a/core/java/android/service/timezone/TEST_MAPPING
+++ b/core/java/android/service/timezone/TEST_MAPPING
@@ -2,10 +2,10 @@
   // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
+      "name": "FrameworksTimeCoreTests",
       "options": [
         {
-          "include-filter": "android.service.timezone."
+          "include-filter": "android.service."
         }
       ]
     },
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 0d5704e..1acc384 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -18,6 +18,7 @@
 
 import static android.view.InsetsSourceProto.FRAME;
 import static android.view.InsetsSourceProto.TYPE;
+import static android.view.InsetsSourceProto.TYPE_NUMBER;
 import static android.view.InsetsSourceProto.VISIBLE;
 import static android.view.InsetsSourceProto.VISIBLE_FRAME;
 import static android.view.WindowInsets.Type.captionBar;
@@ -352,6 +353,7 @@
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(TYPE, WindowInsets.Type.toString(mType));
+        proto.write(TYPE_NUMBER, mType);
         mFrame.dumpDebug(proto, FRAME);
         if (mVisibleFrame != null) {
             mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c9c1f20..d97dfb0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -123,6 +123,7 @@
 import android.graphics.BLASTBufferQueue;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.ForceDarkType;
 import android.graphics.FrameInfo;
 import android.graphics.HardwareRenderer;
 import android.graphics.HardwareRenderer.FrameDrawingCallback;
@@ -1796,7 +1797,7 @@
 
     /** Returns true if force dark should be enabled according to various settings */
     @VisibleForTesting
-    public boolean isForceDarkEnabled() {
+    public @ForceDarkType.ForceDarkTypeDef int determineForceDarkType() {
         if (forceInvertColor()) {
             boolean isForceInvertEnabled = Settings.Secure.getIntForUser(
                     mContext.getContentResolver(),
@@ -1808,7 +1809,7 @@
             // for dark mode in configuration.uiMode. Instead, we assume that the force invert
             // setting will be enabled at the same time dark theme is in the Settings app.
             if (isForceInvertEnabled) {
-                return true;
+                return ForceDarkType.FORCE_INVERT_COLOR_DARK;
             }
         }
 
@@ -1822,12 +1823,12 @@
                     && a.getBoolean(R.styleable.Theme_forceDarkAllowed, forceDarkAllowedDefault);
             a.recycle();
         }
-        return useAutoDark;
+        return useAutoDark ? ForceDarkType.FORCE_DARK : ForceDarkType.NONE;
     }
 
     private void updateForceDarkMode() {
         if (mAttachInfo.mThreadedRenderer == null) return;
-        if (mAttachInfo.mThreadedRenderer.setForceDark(isForceDarkEnabled())) {
+        if (mAttachInfo.mThreadedRenderer.setForceDark(determineForceDarkType())) {
             // TODO: Don't require regenerating all display lists to apply this setting
             invalidateWorld(mView);
         }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index cfec081..c735142 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import static android.content.pm.ActivityInfo.COLOR_MODE_DEFAULT;
-import static android.view.flags.Flags.FLAG_WM_DISPLAY_REFRESH_RATE_TEST;
 import static android.view.View.STATUS_BAR_DISABLE_BACK;
 import static android.view.View.STATUS_BAR_DISABLE_CLOCK;
 import static android.view.View.STATUS_BAR_DISABLE_EXPAND;
@@ -3907,7 +3906,7 @@
          * This value is ignored if {@link #preferredDisplayModeId} is set.
          * @hide
          */
-        @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
+        @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
         @TestApi
         public float preferredMinDisplayRefreshRate;
 
@@ -3917,7 +3916,7 @@
          * This value is ignored if {@link #preferredDisplayModeId} is set.
          * @hide
          */
-        @FlaggedApi(FLAG_WM_DISPLAY_REFRESH_RATE_TEST)
+        @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
         @TestApi
         public float preferredMaxDisplayRefreshRate;
 
diff --git a/core/java/android/view/flags/refresh_rate_flags.aconfig b/core/java/android/view/flags/refresh_rate_flags.aconfig
index cc951cf..a467afe 100644
--- a/core/java/android/view/flags/refresh_rate_flags.aconfig
+++ b/core/java/android/view/flags/refresh_rate_flags.aconfig
@@ -42,11 +42,4 @@
   namespace: "core_graphics"
   description: "Enable the `setFrameRate` callback"
   bug: "299946220"
-}
-
-flag {
-    name: "wm_display_refresh_rate_test"
-    namespace: "core_graphics"
-    description: "Adds WindowManager display refresh rate fields to test API"
-    bug: "304475199"
 }
\ No newline at end of file
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 7f0a651..e20357fa 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -12,6 +12,6 @@
 
 per-file SpellChecker.java = file:../view/inputmethod/OWNERS
 
-per-file RemoteViews* = file:../appwidget/OWNERS
+per-file Remote* = file:../appwidget/OWNERS
 
 per-file Toast.java = juliacr@google.com, jeffdq@google.com
diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java
index d843308..9b396ae 100644
--- a/core/java/android/widget/RemoteCollectionItemsAdapter.java
+++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.appwidget.AppWidgetHostView;
 import android.util.SparseIntArray;
 import android.view.View;
 import android.view.ViewGroup;
@@ -25,8 +26,6 @@
 import android.widget.RemoteViews.InteractionHandler;
 import android.widget.RemoteViews.RemoteCollectionItems;
 
-import com.android.internal.R;
-
 import java.util.stream.IntStream;
 
 /**
@@ -178,40 +177,14 @@
 
         RemoteViews item = mItems.getItemView(position);
         item.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
-        View reapplyView = getViewToReapply(convertView, item);
 
-        // Reapply the RemoteViews if we can.
-        if (reapplyView != null) {
-            try {
-                item.reapply(
-                        parent.getContext(),
-                        reapplyView,
-                        mInteractionHandler,
-                        null /* size */,
-                        mColorResources);
-                return reapplyView;
-            } catch (RuntimeException e) {
-                // We can't reapply for some reason, we'll fallback to an apply and inflate a
-                // new view.
-            }
-        }
-
-        return item.apply(
-                parent.getContext(),
-                parent,
-                mInteractionHandler,
-                null /* size */,
-                mColorResources);
-    }
-
-    /** Returns {@code convertView} if it can be used to reapply {@code item}, or null otherwise. */
-    @Nullable
-    private static View getViewToReapply(@Nullable View convertView, @NonNull RemoteViews item) {
-        if (convertView == null) return null;
-
-        Object layoutIdTag = convertView.getTag(R.id.widget_frame);
-        if (!(layoutIdTag instanceof Integer)) return null;
-
-        return item.getLayoutId() == (Integer) layoutIdTag ? convertView : null;
+        AppWidgetHostView newView = convertView instanceof AppWidgetHostView.AdapterChildHostView
+                widgetChildView
+                ? widgetChildView
+                : new AppWidgetHostView.AdapterChildHostView(parent.getContext());
+        newView.setInteractionHandler(mInteractionHandler);
+        newView.setColorResourcesNoReapply(mColorResources);
+        newView.updateAppWidget(item);
+        return newView;
     }
 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 403b403..a919c00 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6958,13 +6958,13 @@
             View parent = (View) view.getParent();
             // Break the for loop on the first encounter of:
             //    1) an AdapterView,
-            //    2) an AppWidgetHostView that is not a RemoteViewsFrameLayout, or
+            //    2) an AppWidgetHostView that is not a child of an adapter view, or
             //    3) a null parent.
             // 2) and 3) are unexpected and catch the case where a child is not
             // correctly parented in an AdapterView.
             while (parent != null && !(parent instanceof AdapterView<?>)
                     && !((parent instanceof AppWidgetHostView)
-                    && !(parent instanceof RemoteViewsAdapter.RemoteViewsFrameLayout))) {
+                            && !(parent instanceof AppWidgetHostView.AdapterChildHostView))) {
                 parent = (View) parent.getParent();
             }
 
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 61a7599..2f28a87 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -368,7 +368,7 @@
      * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when
      * they are loaded.
      */
-    static class RemoteViewsFrameLayout extends AppWidgetHostView {
+    static class RemoteViewsFrameLayout extends AppWidgetHostView.AdapterChildHostView {
         private final FixedSizeRemoteViewsCache mCache;
 
         public int cacheIndex = -1;
@@ -408,11 +408,6 @@
         }
 
         @Override
-        protected Context getRemoteContextEnsuringCorrectCachedApkPath() {
-            return null;
-        }
-
-        @Override
         protected View getErrorView() {
             // Use the default loading view as the error view.
             return getDefaultView();
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 24dc6db..0ad6c99 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -29,3 +29,10 @@
     description: "Whether to dispatch window resize through ClientTransaction is enabled"
     bug: "301870955"
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "fullscreen_dim_flag"
+    description: "Whether to allow showing fullscreen dim on ActivityEmbedding split"
+    bug: "253533308"
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 9d88a23..d85227f 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -70,9 +70,6 @@
         mPackageFilt = new IntentFilter();
         // Settings app sends the broadcast
         mPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
-        // AMS sends the broadcast
-        mPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-        mPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         mPackageFilt.addDataScheme("package");
         if (isCore) {
             mPackageFilt.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
deleted file mode 100644
index 929c9e8..0000000
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import android.compat.annotation.UnsupportedAppUsage;
-
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.UnsupportedCharsetException;
-
-/**
- * This is a quick and dirty implementation of XmlSerializer that isn't horribly
- * painfully slow like the normal one.  It only does what is needed for the
- * specific XML files being written with it.
- */
-public class FastXmlSerializer implements XmlSerializer {
-    private static final String ESCAPE_TABLE[] = new String[] {
-        "&#0;",   "&#1;",   "&#2;",   "&#3;",  "&#4;",    "&#5;",   "&#6;",  "&#7;",  // 0-7
-        "&#8;",   "&#9;",   "&#10;",  "&#11;", "&#12;",   "&#13;",  "&#14;", "&#15;", // 8-15
-        "&#16;",  "&#17;",  "&#18;",  "&#19;", "&#20;",   "&#21;",  "&#22;", "&#23;", // 16-23
-        "&#24;",  "&#25;",  "&#26;",  "&#27;", "&#28;",   "&#29;",  "&#30;", "&#31;", // 24-31
-        null,     null,     "&quot;", null,     null,     null,     "&amp;",  null,   // 32-39
-        null,     null,     null,     null,     null,     null,     null,     null,   // 40-47
-        null,     null,     null,     null,     null,     null,     null,     null,   // 48-55
-        null,     null,     null,     null,     "&lt;",   null,     "&gt;",   null,   // 56-63
-    };
-
-    private static final int DEFAULT_BUFFER_LEN = 32*1024;
-
-    private static String sSpace = "                                                              ";
-
-    private final int mBufferLen;
-    private final char[] mText;
-    private int mPos;
-
-    private Writer mWriter;
-
-    private OutputStream mOutputStream;
-    private CharsetEncoder mCharset;
-    private ByteBuffer mBytes;
-
-    private boolean mIndent = false;
-    private boolean mInTag;
-
-    private int mNesting = 0;
-    private boolean mLineStart = true;
-
-    @UnsupportedAppUsage
-    public FastXmlSerializer() {
-        this(DEFAULT_BUFFER_LEN);
-    }
-
-    /**
-     * Allocate a FastXmlSerializer with the given internal output buffer size.  If the
-     * size is zero or negative, then the default buffer size will be used.
-     *
-     * @param bufferSize Size in bytes of the in-memory output buffer that the writer will use.
-     */
-    public FastXmlSerializer(int bufferSize) {
-        mBufferLen = (bufferSize > 0) ? bufferSize : DEFAULT_BUFFER_LEN;
-        mText = new char[mBufferLen];
-        mBytes = ByteBuffer.allocate(mBufferLen);
-    }
-
-    private void append(char c) throws IOException {
-        int pos = mPos;
-        if (pos >= (mBufferLen-1)) {
-            flush();
-            pos = mPos;
-        }
-        mText[pos] = c;
-        mPos = pos+1;
-    }
-
-    private void append(String str, int i, final int length) throws IOException {
-        if (length > mBufferLen) {
-            final int end = i + length;
-            while (i < end) {
-                int next = i + mBufferLen;
-                append(str, i, next<end ? mBufferLen : (end-i));
-                i = next;
-            }
-            return;
-        }
-        int pos = mPos;
-        if ((pos+length) > mBufferLen) {
-            flush();
-            pos = mPos;
-        }
-        str.getChars(i, i+length, mText, pos);
-        mPos = pos + length;
-    }
-
-    private void append(char[] buf, int i, final int length) throws IOException {
-        if (length > mBufferLen) {
-            final int end = i + length;
-            while (i < end) {
-                int next = i + mBufferLen;
-                append(buf, i, next<end ? mBufferLen : (end-i));
-                i = next;
-            }
-            return;
-        }
-        int pos = mPos;
-        if ((pos+length) > mBufferLen) {
-            flush();
-            pos = mPos;
-        }
-        System.arraycopy(buf, i, mText, pos, length);
-        mPos = pos + length;
-    }
-
-    private void append(String str) throws IOException {
-        append(str, 0, str.length());
-    }
-
-    private void appendIndent(int indent) throws IOException {
-        indent *= 4;
-        if (indent > sSpace.length()) {
-            indent = sSpace.length();
-        }
-        append(sSpace, 0, indent);
-    }
-
-    private void escapeAndAppendString(final String string) throws IOException {
-        final int N = string.length();
-        final char NE = (char)ESCAPE_TABLE.length;
-        final String[] escapes = ESCAPE_TABLE;
-        int lastPos = 0;
-        int pos;
-        for (pos=0; pos<N; pos++) {
-            char c = string.charAt(pos);
-            if (c >= NE) continue;
-            String escape = escapes[c];
-            if (escape == null) continue;
-            if (lastPos < pos) append(string, lastPos, pos-lastPos);
-            lastPos = pos + 1;
-            append(escape);
-        }
-        if (lastPos < pos) append(string, lastPos, pos-lastPos);
-    }
-
-    private void escapeAndAppendString(char[] buf, int start, int len) throws IOException {
-        final char NE = (char)ESCAPE_TABLE.length;
-        final String[] escapes = ESCAPE_TABLE;
-        int end = start+len;
-        int lastPos = start;
-        int pos;
-        for (pos=start; pos<end; pos++) {
-            char c = buf[pos];
-            if (c >= NE) continue;
-            String escape = escapes[c];
-            if (escape == null) continue;
-            if (lastPos < pos) append(buf, lastPos, pos-lastPos);
-            lastPos = pos + 1;
-            append(escape);
-        }
-        if (lastPos < pos) append(buf, lastPos, pos-lastPos);
-    }
-
-    public XmlSerializer attribute(String namespace, String name, String value) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        append(' ');
-        if (namespace != null) {
-            append(namespace);
-            append(':');
-        }
-        append(name);
-        append("=\"");
-
-        escapeAndAppendString(value);
-        append('"');
-        mLineStart = false;
-        return this;
-    }
-
-    public void cdsect(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void comment(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void docdecl(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void endDocument() throws IOException, IllegalArgumentException, IllegalStateException {
-        flush();
-    }
-
-    public XmlSerializer endTag(String namespace, String name) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        mNesting--;
-        if (mInTag) {
-            append(" />\n");
-        } else {
-            if (mIndent && mLineStart) {
-                appendIndent(mNesting);
-            }
-            append("</");
-            if (namespace != null) {
-                append(namespace);
-                append(':');
-            }
-            append(name);
-            append(">\n");
-        }
-        mLineStart = true;
-        mInTag = false;
-        return this;
-    }
-
-    public void entityRef(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    private void flushBytes() throws IOException {
-        int position;
-        if ((position = mBytes.position()) > 0) {
-            mBytes.flip();
-            mOutputStream.write(mBytes.array(), 0, position);
-            mBytes.clear();
-        }
-    }
-
-    public void flush() throws IOException {
-        //Log.i("PackageManager", "flush mPos=" + mPos);
-        if (mPos > 0) {
-            if (mOutputStream != null) {
-                CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos);
-                CoderResult result = mCharset.encode(charBuffer, mBytes, true);
-                while (true) {
-                    if (result.isError()) {
-                        throw new IOException(result.toString());
-                    } else if (result.isOverflow()) {
-                        flushBytes();
-                        result = mCharset.encode(charBuffer, mBytes, true);
-                        continue;
-                    }
-                    break;
-                }
-                flushBytes();
-                mOutputStream.flush();
-            } else {
-                mWriter.write(mText, 0, mPos);
-                mWriter.flush();
-            }
-            mPos = 0;
-        }
-    }
-
-    public int getDepth() {
-        throw new UnsupportedOperationException();
-    }
-
-    public boolean getFeature(String name) {
-        throw new UnsupportedOperationException();
-    }
-
-    public String getName() {
-        throw new UnsupportedOperationException();
-    }
-
-    public String getNamespace() {
-        throw new UnsupportedOperationException();
-    }
-
-    public String getPrefix(String namespace, boolean generatePrefix)
-            throws IllegalArgumentException {
-        throw new UnsupportedOperationException();
-    }
-
-    public Object getProperty(String name) {
-        throw new UnsupportedOperationException();
-    }
-
-    public void ignorableWhitespace(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void processingInstruction(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void setFeature(String name, boolean state) throws IllegalArgumentException,
-            IllegalStateException {
-        if (name.equals("http://xmlpull.org/v1/doc/features.html#indent-output")) {
-            mIndent = true;
-            return;
-        }
-        throw new UnsupportedOperationException();
-    }
-
-    public void setOutput(OutputStream os, String encoding) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        if (os == null)
-            throw new IllegalArgumentException();
-        if (true) {
-            try {
-                mCharset = Charset.forName(encoding).newEncoder()
-                        .onMalformedInput(CodingErrorAction.REPLACE)
-                        .onUnmappableCharacter(CodingErrorAction.REPLACE);
-            } catch (IllegalCharsetNameException e) {
-                throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
-                        encoding).initCause(e));
-            } catch (UnsupportedCharsetException e) {
-                throw (UnsupportedEncodingException) (new UnsupportedEncodingException(
-                        encoding).initCause(e));
-            }
-            mOutputStream = os;
-        } else {
-            setOutput(
-                encoding == null
-                    ? new OutputStreamWriter(os)
-                    : new OutputStreamWriter(os, encoding));
-        }
-    }
-
-    public void setOutput(Writer writer) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        mWriter = writer;
-    }
-
-    public void setPrefix(String prefix, String namespace) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void setProperty(String name, Object value) throws IllegalArgumentException,
-            IllegalStateException {
-        throw new UnsupportedOperationException();
-    }
-
-    public void startDocument(String encoding, Boolean standalone) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        append("<?xml version='1.0' encoding='utf-8'");
-        if (standalone != null) {
-            append(" standalone='" + (standalone ? "yes" : "no") + "'");
-        }
-        append(" ?>\n");
-        mLineStart = true;
-    }
-
-    public XmlSerializer startTag(String namespace, String name) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        if (mInTag) {
-            append(">\n");
-        }
-        if (mIndent) {
-            appendIndent(mNesting);
-        }
-        mNesting++;
-        append('<');
-        if (namespace != null) {
-            append(namespace);
-            append(':');
-        }
-        append(name);
-        mInTag = true;
-        mLineStart = false;
-        return this;
-    }
-
-    public XmlSerializer text(char[] buf, int start, int len) throws IOException,
-            IllegalArgumentException, IllegalStateException {
-        if (mInTag) {
-            append(">");
-            mInTag = false;
-        }
-        escapeAndAppendString(buf, start, len);
-        if (mIndent) {
-            mLineStart = buf[start+len-1] == '\n';
-        }
-        return this;
-    }
-
-    public XmlSerializer text(String text) throws IOException, IllegalArgumentException,
-            IllegalStateException {
-        if (mInTag) {
-            append(">");
-            mInTag = false;
-        }
-        escapeAndAppendString(text);
-        if (mIndent) {
-            mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n');
-        }
-        return this;
-    }
-
-}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 8e619a8..404fa39 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -593,6 +593,7 @@
     optional bool seamless_rotating = 13;
     optional int64 finish_seamless_rotate_frame_number = 14;
     optional bool controllable = 15;
+    optional WindowStateProto source_window_state = 16;
 }
 
 message ImeInsetsSourceProviderProto {
diff --git a/core/proto/android/view/insetssource.proto b/core/proto/android/view/insetssource.proto
index 41b9f43..e6c6d59 100644
--- a/core/proto/android/view/insetssource.proto
+++ b/core/proto/android/view/insetssource.proto
@@ -30,4 +30,5 @@
     optional .android.graphics.RectProto frame = 2;
     optional .android.graphics.RectProto visible_frame = 3;
     optional bool visible = 4;
+    optional int32 type_number = 5;
 }
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 862e537..f827d28 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -832,6 +832,13 @@
          is set to true. -->
     <bool name="config_remoteInsetsControllerSystemBarsCanBeShownByUserAction">false</bool>
 
+    <!-- To change the default behavior of how the insets get involved when calculating
+         configuration. This will no longer consider status bar and display cutout, and only
+         exclude navigation bars from the screen sizes. This is useful when the display is close to
+         square and the system bars may cause the orientation with non-preferred value.
+         -->
+    <bool name="config_decoupleStatusBarAndDisplayCutoutFromScreenSize">false</bool>
+
     <!-- HDMI behavior -->
 
     <!-- The number of degrees to rotate the display when the device has HDMI connected
@@ -1256,6 +1263,11 @@
     -->
     <bool name="config_shortPressEarlyOnPower">false</bool>
 
+    <!-- Whether a single short press on STEM_PRIMARY should be launched without multi-press delay.
+        This works similarly as config_shortPressEarlyOnPower but for STEM_PRIMARY.
+    -->
+    <bool name="config_shortPressEarlyOnStemPrimary">false</bool>
+
     <!-- Control the behavior of the search key.
             0 - Launch default search activity
             1 - Launch target activity defined by config_searchKeyTargetActivity
@@ -2707,6 +2719,12 @@
          turned off and the screen off animation has been performed. -->
     <bool name="config_dozeAfterScreenOffByDefault">false</bool>
 
+    <!-- If true, bright policy will be applied when we have entered dozing wakefulness but haven't
+         started doze component. This can be used to suppress the temporary dim state before
+         starting a dream service. This is typically used together with
+         config_maximumScreenDimDuration set to 0 to completely suppress dim effect. -->
+    <bool name="config_brightWhenDozing">false</bool>
+
     <!-- Doze: should the TYPE_PICK_UP_GESTURE sensor be used as a pulse signal. -->
     <bool name="config_dozePulsePickup">false</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8748ca1..2f3b510 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -473,6 +473,7 @@
   <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" />
   <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" />
   <java-symbol type="bool" name="config_shortPressEarlyOnPower" />
+  <java-symbol type="bool" name="config_shortPressEarlyOnStemPrimary" />
   <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" />
   <java-symbol type="integer" name="config_searchKeyBehavior" />
   <java-symbol type="string" name="config_searchKeyTargetActivity" />
@@ -1732,6 +1733,7 @@
   <java-symbol type="bool" name="config_enableLockScreenRotation" />
   <java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
   <java-symbol type="bool" name="config_remoteInsetsControllerSystemBarsCanBeShownByUserAction" />
+  <java-symbol type="bool" name="config_decoupleStatusBarAndDisplayCutoutFromScreenSize" />
   <java-symbol type="bool" name="config_lidControlsScreenLock" />
   <java-symbol type="bool" name="config_lidControlsSleep" />
   <java-symbol type="bool" name="config_lockDayNightMode" />
@@ -1955,6 +1957,7 @@
   <java-symbol type="bool" name="config_enableNightMode" />
   <java-symbol type="bool" name="config_tintNotificationActionButtons" />
   <java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
+  <java-symbol type="bool" name="config_brightWhenDozing" />
   <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
   <java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
   <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
diff --git a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
index 282fdad..ba2ea88 100644
--- a/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
+++ b/core/tests/coretests/src/android/app/AutomaticZenRuleTest.java
@@ -125,6 +125,9 @@
             Field configActivity = Class.forName(CLASS).getDeclaredField("configurationActivity");
             configActivity.setAccessible(true);
             configActivity.set(rule, new ComponentName(longString, longString));
+            Field trigger = Class.forName(CLASS).getDeclaredField("mTriggerDescription");
+            trigger.setAccessible(true);
+            trigger.set(rule, longString);
         } catch (NoSuchFieldException e) {
             fail(e.toString());
         } catch (ClassNotFoundException e) {
@@ -149,5 +152,6 @@
                 fromParcel.getOwner().getPackageName().length());
         assertEquals(AutomaticZenRule.MAX_STRING_LENGTH,
                 fromParcel.getOwner().getClassName().length());
+        assertEquals(AutomaticZenRule.MAX_DESC_LENGTH, rule.getTriggerDescription().length());
     }
 }
diff --git a/core/tests/coretests/src/android/app/time/TEST_MAPPING b/core/tests/coretests/src/android/app/time/TEST_MAPPING
deleted file mode 100644
index 9d711a2..0000000
--- a/core/tests/coretests/src/android/app/time/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.time."
-        }
-      ]
-    }
-  ]
-}
diff --git a/core/tests/coretests/src/android/app/timedetector/OWNERS b/core/tests/coretests/src/android/app/timedetector/OWNERS
deleted file mode 100644
index c612473..0000000
--- a/core/tests/coretests/src/android/app/timedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/app/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING
deleted file mode 100644
index 6c4d48d..0000000
--- a/core/tests/coretests/src/android/app/timedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.timedetector."
-        }
-      ]
-    }
-  ]
-}
diff --git a/core/tests/coretests/src/android/app/timezonedetector/OWNERS b/core/tests/coretests/src/android/app/timezonedetector/OWNERS
deleted file mode 100644
index 2e9c324..0000000
--- a/core/tests/coretests/src/android/app/timezonedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/app/timezonedetector/OWNERS
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING b/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING
deleted file mode 100644
index 8872f64..0000000
--- a/core/tests/coretests/src/android/app/timezonedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.timezonedetector."
-        }
-      ]
-    }
-  ]
-}
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index 7ebda00..bec72d9 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -10,7 +10,6 @@
         {"include-filter": "android.service.notification"},
         {"include-filter": "android.service.quicksettings"},
         {"include-filter": "android.service.settings.suggestions"},
-        {"include-filter": "android.service.timezone"},
         {"exclude-annotation": "org.junit.Ignore"}
       ]
     }
diff --git a/core/tests/coretests/src/android/service/timezone/OWNERS b/core/tests/coretests/src/android/service/timezone/OWNERS
deleted file mode 100644
index 8116388..0000000
--- a/core/tests/coretests/src/android/service/timezone/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /core/java/android/service/timezone/OWNERS
diff --git a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING b/core/tests/coretests/src/android/service/timezone/TEST_MAPPING
deleted file mode 100644
index 46f476f..0000000
--- a/core/tests/coretests/src/android/service/timezone/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.service.timezone."
-        }
-      ]
-    }
-  ]
-}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index e7117a7..dfe6cf8 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -49,6 +49,7 @@
 import android.app.Instrumentation;
 import android.app.UiModeManager;
 import android.content.Context;
+import android.graphics.ForceDarkType;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
 import android.os.SystemProperties;
@@ -593,7 +594,7 @@
                 mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
         );
 
-        assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+        assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
     }
 
     @Test
@@ -613,7 +614,8 @@
                 mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
         );
 
-        assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+        assertThat(mViewRootImpl.determineForceDarkType())
+                .isEqualTo(ForceDarkType.FORCE_INVERT_COLOR_DARK);
     }
 
     @Test
@@ -634,7 +636,7 @@
                 mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
         );
 
-        assertThat(mViewRootImpl.isForceDarkEnabled()).isFalse();
+        assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.NONE);
     }
 
     @Test
@@ -654,7 +656,7 @@
                 mViewRootImpl.updateConfiguration(sContext.getDisplayNoVerify().getDisplayId())
         );
 
-        assertThat(mViewRootImpl.isForceDarkEnabled()).isTrue();
+        assertThat(mViewRootImpl.determineForceDarkType()).isEqualTo(ForceDarkType.FORCE_DARK);
     }
 
     private boolean setForceDarkSysProp(boolean isForceDarkEnabled) {
diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp
new file mode 100644
index 0000000..51181a8
--- /dev/null
+++ b/core/tests/timetests/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "FrameworksTimeCoreTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "device-time-shell-utils",
+        "junit",
+        "junit-params",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "truth",
+    ],
+    libs: ["android.test.runner"],
+    certificate: "platform",
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/timetests/AndroidManifest.xml b/core/tests/timetests/AndroidManifest.xml
new file mode 100644
index 0000000..330e05f
--- /dev/null
+++ b/core/tests/timetests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.coretests.time">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+            android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.frameworks.coretests.time"
+            android:label="Frameworks Time Core Tests" />
+
+</manifest>
diff --git a/core/tests/timetests/AndroidTest.xml b/core/tests/timetests/AndroidTest.xml
new file mode 100644
index 0000000..d2d1255
--- /dev/null
+++ b/core/tests/timetests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Time Core Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksTimeCoreTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.coretests.time" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/core/tests/coretests/src/android/app/time/OWNERS b/core/tests/timetests/OWNERS
similarity index 100%
rename from core/tests/coretests/src/android/app/time/OWNERS
rename to core/tests/timetests/OWNERS
diff --git a/core/tests/timetests/TEST_MAPPING b/core/tests/timetests/TEST_MAPPING
new file mode 100644
index 0000000..5748044
--- /dev/null
+++ b/core/tests/timetests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeCoreTests"
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java b/core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/DetectorStatusTypesTest.java
rename to core/tests/timetests/src/android/app/time/DetectorStatusTypesTest.java
diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/time/ExternalTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
rename to core/tests/timetests/src/android/app/time/LocationTimeZoneAlgorithmStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
similarity index 93%
rename from core/tests/coretests/src/android/app/time/ParcelableTestSupport.java
rename to core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
index 13e5e14..d2c9c3a 100644
--- a/core/tests/coretests/src/android/app/time/ParcelableTestSupport.java
+++ b/core/tests/timetests/src/android/app/time/ParcelableTestSupport.java
@@ -47,6 +47,10 @@
         return toReturn;
     }
 
+    /**
+     * Asserts that the parameter can be parceled and unparceled and return an object considered
+     * equal to the original.
+     */
     public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
         assertEqualsAndHashCode(instance, roundTripParcelable(instance));
     }
diff --git a/core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java b/core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
rename to core/tests/timetests/src/android/app/time/TelephonyTimeZoneAlgorithmStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
rename to core/tests/timetests/src/android/app/time/TimeCapabilitiesTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeManagerTest.java b/core/tests/timetests/src/android/app/time/TimeManagerTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeManagerTest.java
rename to core/tests/timetests/src/android/app/time/TimeManagerTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeStateTest.java b/core/tests/timetests/src/android/app/time/TimeStateTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeStateTest.java
rename to core/tests/timetests/src/android/app/time/TimeStateTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java b/core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneDetectorStatusTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneDetectorStatusTest.java
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java b/core/tests/timetests/src/android/app/time/TimeZoneStateTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
rename to core/tests/timetests/src/android/app/time/TimeZoneStateTest.java
diff --git a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java b/core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
rename to core/tests/timetests/src/android/app/time/UnixEpochTimeTest.java
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/timedetector/ManualTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java b/core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
rename to core/tests/timetests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
rename to core/tests/timetests/src/android/app/timezonedetector/ManualTimeZoneSuggestionTest.java
diff --git a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
similarity index 89%
rename from core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java
rename to core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
index 4efaed1..59a7304 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/ShellCommandTestSupport.java
+++ b/core/tests/timetests/src/android/app/timezonedetector/ShellCommandTestSupport.java
@@ -29,10 +29,17 @@
 public final class ShellCommandTestSupport {
     private ShellCommandTestSupport() {}
 
+    /**
+     * Returns a {@link ShellCommand} from the supplied String, where elements of the command are
+     * separated with spaces. No escaping is performed.
+     */
     public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
         return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" ")));
     }
 
+    /**
+     * Returns a {@link ShellCommand} from the supplied list of command line elements.
+     */
     public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
         ShellCommand command = mock(ShellCommand.class);
         class ArgProvider {
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
rename to core/tests/timetests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java
similarity index 100%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderEventTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderEventTest.java
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
similarity index 98%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
index 9b24559..cf37db6 100644
--- a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderStatusTest.java
+++ b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderStatusTest.java
@@ -32,6 +32,9 @@
 
 import androidx.test.filters.SmallTest;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -39,9 +42,6 @@
 import java.util.List;
 import java.util.stream.IntStream;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 /** Non-SDK tests. See CTS for SDK API tests. */
 @RunWith(JUnitParamsRunner.class)
 @SmallTest
@@ -83,6 +83,7 @@
                 providerStatus.couldEnableTelephonyFallback());
     }
 
+    /** Parameters for {@link #couldEnableTelephonyFallback}. */
     public static Integer[][] couldEnableTelephonyFallbackParams() {
         List<Integer[]> params = new ArrayList<>();
         @DependencyStatus int[] dependencyStatuses =
diff --git a/core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java b/core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
similarity index 100%
rename from core/tests/coretests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
rename to core/tests/timetests/src/android/service/timezone/TimeZoneProviderSuggestionTest.java
diff --git a/framework-minus-apex-ravenwood-policies.txt b/framework-minus-apex-ravenwood-policies.txt
index 8e76fd2..48c0a2d 100644
--- a/framework-minus-apex-ravenwood-policies.txt
+++ b/framework-minus-apex-ravenwood-policies.txt
@@ -71,8 +71,9 @@
 # Misc
 class android.util.Dumpable stubclass
 class android.util.DebugUtils stubclass
-class android.util.UtilConfig stubclass
+class android.util.MathUtils stubclass
 class android.util.Patterns stubclass
+class android.util.UtilConfig stubclass
 
 # Internals
 class com.android.internal.util.ArrayUtils stubclass
@@ -89,3 +90,26 @@
 class com.android.internal.util.LineBreakBufferedWriter stubclass
 class com.android.internal.util.Preconditions stubclass
 class com.android.internal.util.StringPool stubclass
+
+# Parcel
+class android.os.Parcel stubclass
+    method writeException (Ljava/lang/Exception;)V @writeException$ravenwood
+    method writeNoException ()V @writeNoException$ravenwood
+class android.os.Parcel !com.android.hoststubgen.nativesubstitution.Parcel_host
+
+class android.os.Parcelable stubclass
+class android.os.ParcelFormatException stubclass
+class android.os.BadParcelableException stubclass
+class android.os.BadTypeParcelableException stubclass
+
+# Binder: just enough to construct, no further functionality
+class android.os.Binder stub
+    method <init> ()V stub
+    method <init> (Ljava/lang/String;)V stub
+    method isDirectlyHandlingTransaction ()Z stub
+    method isDirectlyHandlingTransactionNative ()Z @isDirectlyHandlingTransactionNative$ravenwood
+    method getNativeBBinderHolder ()J @getNativeBBinderHolder$ravenwood
+
+# Containers
+class android.os.BaseBundle stubclass
+class android.os.Bundle stubclass
diff --git a/graphics/java/android/graphics/ForceDarkType.java b/graphics/java/android/graphics/ForceDarkType.java
new file mode 100644
index 0000000..396b037
--- /dev/null
+++ b/graphics/java/android/graphics/ForceDarkType.java
@@ -0,0 +1,60 @@
+/*
+ * 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.graphics;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The style of force dark to use in {@link HardwareRenderer}.
+ *
+ * You must keep this in sync with the C++ enum ForceDarkType in
+ * frameworks/base/libs/hwui/utils/ForceDark.h
+ *
+ * @hide
+ */
+public class ForceDarkType {
+    /**
+     * Force dark disabled: normal, default operation.
+     *
+     * @hide
+     */
+    public static final int NONE = 0;
+
+    /**
+     * Use force dark
+     * @hide
+     */
+    public static final int FORCE_DARK = 1;
+
+    /**
+     * Force force-dark. {@see Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED}
+     * @hide */
+    public static final int FORCE_INVERT_COLOR_DARK = 2;
+
+    /** @hide */
+    @IntDef({
+        NONE,
+        FORCE_DARK,
+        FORCE_INVERT_COLOR_DARK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ForceDarkTypeDef {}
+
+}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 8cd262e..20e393e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -182,7 +182,7 @@
     /** @hide */
     protected RenderNode mRootNode;
     private boolean mOpaque = true;
-    private boolean mForceDark = false;
+    private int mForceDark = ForceDarkType.NONE;
     private @ActivityInfo.ColorMode int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
     private float mDesiredSdrHdrRatio = 1f;
 
@@ -571,10 +571,10 @@
      * Whether or not the force-dark feature should be used for this renderer.
      * @hide
      */
-    public boolean setForceDark(boolean enable) {
-        if (mForceDark != enable) {
-            mForceDark = enable;
-            nSetForceDark(mNativeProxy, enable);
+    public boolean setForceDark(@ForceDarkType.ForceDarkTypeDef int type) {
+        if (mForceDark != type) {
+            mForceDark = type;
+            nSetForceDark(mNativeProxy, type);
             return true;
         }
         return false;
@@ -1597,7 +1597,7 @@
 
     private static native void nAllocateBuffers(long nativeProxy);
 
-    private static native void nSetForceDark(long nativeProxy, boolean enabled);
+    private static native void nSetForceDark(long nativeProxy, int type);
 
     private static native void nSetDisplayDensityDpi(int densityDpi);
 
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 0e59e9a..29bdd5c 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -28,3 +28,10 @@
     description: "Enables invoking split contextually"
     bug: "276361926"
 }
+
+flag {
+    name: "enable_taskbar_navbar_unification"
+    namespace: "multitasking"
+    description: "Enables taskbar / navbar unification"
+    bug: "309671494"
+}
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b556150e..3e66bbb 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -72,7 +72,7 @@
     <!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead  [CHAR LIMIT=NONE] -->
     <string name="dock_non_resizeble_failed_to_dock_text">App does not support split screen</string>
     <!-- Warning message when we try to dock an app not supporting multiple instances split into multiple sides [CHAR LIMIT=NONE] -->
-    <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window.</string>
+    <string name="dock_multi_instances_not_supported_text">This app can only be opened in 1 window</string>
     <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
     <string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
     <!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
index 42dc19c..7749394 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -130,7 +130,14 @@
                     mStartBounds);
             mCustomizer.customizeStatusBarAppearance(region);
         } else {
-            mCustomizer.customizeStatusBarAppearance(null);
+            resetStatusBarCustomization();
         }
     }
+
+    /**
+     * Resets the statusbar customization
+     */
+    public void resetStatusBarCustomization() {
+        mCustomizer.customizeStatusBarAppearance(null);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index a596cef..f90e3f0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -21,6 +21,7 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
 import static com.android.wm.shell.back.BackAnimationConstants.PROGRESS_COMMIT_THRESHOLD;
+import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 
 import android.animation.Animator;
@@ -191,6 +192,8 @@
         // Draw background with task background color.
         mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
                 mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction);
+        setEnteringProgress(0);
+        setLeavingProgress(0);
     }
 
     private void applyTransform(SurfaceControl leash, RectF targetRect, float targetAlpha) {
@@ -272,12 +275,16 @@
         valueAnimator.addUpdateListener(animation -> {
             float progress = animation.getAnimatedFraction();
             updatePostCommitEnteringAnimation(progress);
+            if (progress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD) {
+                mBackground.resetStatusBarCustomization();
+            }
             mTransaction.apply();
         });
 
         valueAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                mBackground.resetStatusBarCustomization();
                 finishAnimation();
             }
         });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 5a15674..80fc3a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -18,8 +18,10 @@
 
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.window.BackEvent.EDGE_RIGHT;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_TASK;
+import static com.android.wm.shell.back.BackAnimationConstants.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 
 import android.animation.Animator;
@@ -100,6 +102,7 @@
     private RemoteAnimationTarget mClosingTarget;
     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
     private boolean mBackInProgress = false;
+    private boolean mIsRightEdge;
     private final PointF mTouchPos = new PointF();
     private IRemoteAnimationFinishedCallback mFinishCallback;
     private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
@@ -171,7 +174,12 @@
         // Move the window along the Y axis.
         float scaledTop = (height - scaledHeight) * 0.5f + deltaY;
         // Move the window along the X axis.
-        float right = width - (progress * mVerticalMargin);
+        float right;
+        if (mIsRightEdge) {
+            right = (width - scaledWidth) * 0.5f + scaledWidth;
+        } else {
+            right = width - (progress * mVerticalMargin);
+        }
         float left = right - scaledWidth;
 
         mClosingCurrentRect.set(left, scaledTop, right, scaledTop + scaledHeight);
@@ -261,6 +269,7 @@
 
     private void onGestureProgress(@NonNull BackEvent backEvent) {
         if (!mBackInProgress) {
+            mIsRightEdge = backEvent.getSwipeEdge() == EDGE_RIGHT;
             mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
             mBackInProgress = true;
         }
@@ -287,12 +296,16 @@
             float progress = animation.getAnimatedFraction();
             updatePostCommitEnteringAnimation(progress);
             updatePostCommitClosingAnimation(progress);
+            if (progress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD) {
+                mBackground.resetStatusBarCustomization();
+            }
             mTransaction.apply();
         });
 
         valueAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                mBackground.resetStatusBarCustomization();
                 finishAnimation();
             }
         });
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 0890861..e63bbc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -252,12 +252,7 @@
                 Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e);
             }
             nextFinishCallback.onTransitionFinished(null);
-        } else if (nextInfo.getType() == TRANSIT_SLEEP) {
-            // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep
-            // token is held. In cases where keyguard is showing, we are running the animation for
-            // the device sleeping/waking, so it's best to ignore this and keep playing anyway.
-            return;
-        } else if (handles(nextInfo)) {
+        } else {
             // In all other cases, fast-forward to let the next queued transition start playing.
             finishAnimationImmediately(currentTransition, playing);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 57cc28d..8eb4a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -427,10 +427,10 @@
                     new PipContentOverlay.PipSnapshotOverlay(snapshot, sourceRectHint));
         }
 
-        void setAppIconContentOverlay(Context context, Rect bounds, ActivityInfo activityInfo,
-                int appIconSizePx) {
+        void setAppIconContentOverlay(Context context, Rect appBounds, Rect destinationBounds,
+                ActivityInfo activityInfo, int appIconSizePx) {
             reattachContentOverlay(
-                    new PipContentOverlay.PipAppIconOverlay(context, bounds,
+                    new PipContentOverlay.PipAppIconOverlay(context, appBounds, destinationBounds,
                             new IconProvider(context).getIcon(activityInfo), appIconSizePx));
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index c701b95..850b06a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -180,20 +180,34 @@
         private final Context mContext;
         private final int mAppIconSizePx;
         private final Rect mAppBounds;
+        private final int mOverlayHalfSize;
         private final Matrix mTmpTransform = new Matrix();
         private final float[] mTmpFloat9 = new float[9];
 
         private Bitmap mBitmap;
 
-        public PipAppIconOverlay(Context context, Rect appBounds,
+        public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
                 Drawable appIcon, int appIconSizePx) {
             mContext = context;
             final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
                     MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
             mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
-            mAppBounds = new Rect(appBounds);
-            mBitmap = Bitmap.createBitmap(appBounds.width(), appBounds.height(),
-                    Bitmap.Config.ARGB_8888);
+
+            final int appWidth = appBounds.width();
+            final int appHeight = appBounds.height();
+
+            // In order to have the overlay always cover the pip window during the transition, the
+            // overlay will be drawn with the max size of the start and end bounds in different
+            // rotation.
+            final int overlaySize = Math.max(Math.max(appWidth, appHeight),
+                    Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+            mOverlayHalfSize = overlaySize >> 1;
+
+            // When the activity is in the secondary split, make sure the scaling center is not
+            // offset.
+            mAppBounds = new Rect(0, 0, appWidth, appHeight);
+
+            mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
             prepareAppIconOverlay(appIcon);
             mLeash = new SurfaceControl.Builder(new SurfaceSession())
                     .setCallsite(TAG)
@@ -215,12 +229,19 @@
         public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
                 Rect currentBounds, float fraction) {
             mTmpTransform.reset();
+            // In order for the overlay to always cover the pip window, the overlay may have a
+            // size larger than the pip window. Make sure that app icon is at the center.
+            final int appBoundsCenterX = mAppBounds.centerX();
+            final int appBoundsCenterY = mAppBounds.centerY();
+            mTmpTransform.setTranslate(
+                    appBoundsCenterX - mOverlayHalfSize,
+                    appBoundsCenterY - mOverlayHalfSize);
             // Scale back the bitmap with the pivot point at center.
             mTmpTransform.postScale(
                     (float) mAppBounds.width() / currentBounds.width(),
                     (float) mAppBounds.height() / currentBounds.height(),
-                    mAppBounds.centerX(),
-                    mAppBounds.centerY());
+                    appBoundsCenterX,
+                    appBoundsCenterY);
             atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
                     .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
         }
@@ -253,10 +274,10 @@
                 ta.recycle();
             }
             final Rect appIconBounds = new Rect(
-                    mAppBounds.centerX() - mAppIconSizePx / 2,
-                    mAppBounds.centerY() - mAppIconSizePx / 2,
-                    mAppBounds.centerX() + mAppIconSizePx / 2,
-                    mAppBounds.centerY() + mAppIconSizePx / 2);
+                    mOverlayHalfSize - mAppIconSizePx / 2,
+                    mOverlayHalfSize - mAppIconSizePx / 2,
+                    mOverlayHalfSize + mAppIconSizePx / 2,
+                    mOverlayHalfSize + mAppIconSizePx / 2);
             appIcon.setBounds(appIconBounds);
             appIcon.draw(canvas);
             mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 9e8f9c6..083cd08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1718,7 +1718,7 @@
             sourceHintRect = computeRotatedBounds(rotationDelta, direction, destinationBounds,
                     sourceHintRect);
         }
-        Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
+        final Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
                 ? mPipBoundsState.getBounds() : currentBounds;
         final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null
                 && mPipAnimationController.getCurrentAnimator().isRunning();
@@ -1741,7 +1741,7 @@
                 final boolean hasTopActivityInfo = mTaskInfo.topActivityInfo != null;
                 if (hasTopActivityInfo) {
                     animator.setAppIconContentOverlay(
-                            mContext, currentBounds, mTaskInfo.topActivityInfo,
+                            mContext, currentBounds, destinationBounds, mTaskInfo.topActivityInfo,
                             mPipBoundsState.getLauncherState().getAppIconSizePx());
                 } else {
                     ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 018d674..d5b29e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -916,7 +916,7 @@
                 final boolean hasTopActivityInfo = taskInfo.topActivityInfo != null;
                 if (hasTopActivityInfo) {
                     animator.setAppIconContentOverlay(
-                            mContext, currentBounds, taskInfo.topActivityInfo,
+                            mContext, currentBounds, destinationBounds, taskInfo.topActivityInfo,
                             mPipBoundsState.getLauncherState().getAppIconSizePx());
                 } else {
                     ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 386983c..4d11dfb 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -124,10 +124,6 @@
         ":WMShellFlickerTestsPipCommon-src",
     ],
     static_libs: ["WMShellFlickerTestsBase"],
-    test_suites: [
-        "device-tests",
-        "csuite",
-    ],
 }
 
 csuite_test {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index ad600d0..6c3172a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -46,7 +46,7 @@
 
 #ifndef __ANDROID__ // Layoutlib does not compile HWUIProperties.sysprop as it depends on cutils properties
 std::optional<bool> use_vulkan() {
-    return base::GetBoolProperty("ro.hwui.use_vulkan", false);
+    return base::GetBoolProperty("ro.hwui.use_vulkan", true);
 }
 
 std::optional<std::int32_t> render_ahead() {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d28bb49..3e131bc 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -40,6 +40,7 @@
 #ifdef __ANDROID__
 #include "include/gpu/ganesh/SkImageGanesh.h"
 #endif
+#include "utils/ForceDark.h"
 #include "utils/MathUtils.h"
 #include "utils/StringUtils.h"
 
@@ -403,16 +404,21 @@
     deleteDisplayList(observer, info);
     mDisplayList = std::move(mStagingDisplayList);
     if (mDisplayList) {
-        WebViewSyncData syncData {
-            .applyForceDark = info && !info->disableForceDark
-        };
+        WebViewSyncData syncData{.applyForceDark = shouldEnableForceDark(info)};
         mDisplayList.syncContents(syncData);
         handleForceDark(info);
     }
 }
 
+inline bool RenderNode::shouldEnableForceDark(TreeInfo* info) {
+    return CC_UNLIKELY(
+            info &&
+            (!info->disableForceDark ||
+             info->forceDarkType == android::uirenderer::ForceDarkType::FORCE_INVERT_COLOR_DARK));
+}
+
 void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
-    if (CC_LIKELY(!info || info->disableForceDark)) {
+    if (!shouldEnableForceDark(info)) {
         return;
     }
     auto usage = usageHint();
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c959db3..1f3834be 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -233,6 +233,7 @@
     void syncProperties();
     void syncDisplayList(TreeObserver& observer, TreeInfo* info);
     void handleForceDark(TreeInfo* info);
+    bool shouldEnableForceDark(TreeInfo* info);
 
     void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp
index 750f869..717157c 100644
--- a/libs/hwui/TreeInfo.cpp
+++ b/libs/hwui/TreeInfo.cpp
@@ -24,7 +24,8 @@
         : mode(mode)
         , prepareTextures(mode == MODE_FULL)
         , canvasContext(canvasContext)
-        , disableForceDark(canvasContext.useForceDark() ? 0 : 1)
+        , disableForceDark(canvasContext.getForceDarkType() == ForceDarkType::NONE ? 1 : 0)
+        , forceDarkType(canvasContext.getForceDarkType())
         , screenSize(canvasContext.getNextFrameSize()) {}
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index ea25f68..88449f3 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -24,6 +24,7 @@
 #include "Properties.h"
 #include "SkSize.h"
 #include "SkippedFrameInfo.h"
+#include "utils/ForceDark.h"
 #include "utils/Macros.h"
 
 namespace android {
@@ -97,6 +98,7 @@
     bool updateWindowPositions = false;
 
     int disableForceDark;
+    ForceDarkType forceDarkType = ForceDarkType::NONE;
 
     const SkISize screenSize;
 
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 422ffea..d15b1680 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -35,6 +35,7 @@
 #include <gui/TraceUtils.h>
 #include <include/encode/SkPngEncoder.h>
 #include <inttypes.h>
+#include <log/log.h>
 #include <media/NdkImage.h>
 #include <media/NdkImageReader.h>
 #include <nativehelper/JNIPlatformHelp.h>
@@ -53,11 +54,11 @@
 
 #include <algorithm>
 #include <atomic>
-#include <log/log.h>
 #include <vector>
 
 #include "JvmErrorReporter.h"
 #include "android_graphics_HardwareRendererObserver.h"
+#include "utils/ForceDark.h"
 
 namespace android {
 
@@ -824,10 +825,10 @@
     proxy->allocateBuffers();
 }
 
-static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jboolean enable) {
+static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, jlong proxyPtr,
+                                                       jint type) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setForceDark(enable);
+    proxy->setForceDark(static_cast<ForceDarkType>(type));
 }
 
 static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
@@ -1016,7 +1017,7 @@
         {"nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess},
         {"nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority},
         {"nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers},
-        {"nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark},
+        {"nSetForceDark", "(JI)V", (void*)android_view_ThreadedRenderer_setForceDark},
         {"nSetDisplayDensityDpi", "(I)V",
          (void*)android_view_ThreadedRenderer_setDisplayDensityDpi},
         {"nInitDisplayInfo", "(IIFIJJZZ)V", (void*)android_view_ThreadedRenderer_initDisplayInfo},
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 7fac0c9..9e3bb79 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -969,6 +969,7 @@
     // buildLayer() will leave the tree in an unknown state, so we must stop drawing
     stopDrawing();
 
+    ScopedActiveContext activeContext(this);
     TreeInfo info(TreeInfo::MODE_FULL, *this);
     info.damageAccumulator = &mDamageAccumulator;
     info.layerUpdateQueue = &mLayerUpdateQueue;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 37e4f7ec..be9b649 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -46,6 +46,7 @@
 #include "renderstate/RenderState.h"
 #include "renderthread/RenderTask.h"
 #include "renderthread/RenderThread.h"
+#include "utils/ForceDark.h"
 #include "utils/RingBuffer.h"
 
 namespace android {
@@ -194,11 +195,9 @@
         mRenderPipeline->setPictureCapturedCallback(callback);
     }
 
-    void setForceDark(bool enable) { mUseForceDark = enable; }
+    void setForceDark(ForceDarkType type) { mForceDarkType = type; }
 
-    bool useForceDark() {
-        return mUseForceDark;
-    }
+    ForceDarkType getForceDarkType() { return mForceDarkType; }
 
     SkISize getNextFrameSize() const;
 
@@ -321,7 +320,7 @@
     nsecs_t mLastDropVsync = 0;
 
     bool mOpaque;
-    bool mUseForceDark = false;
+    ForceDarkType mForceDarkType = ForceDarkType::NONE;
     LightInfo mLightInfo;
     LightGeometry mLightGeometry = {{0, 0, 0}, 0};
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index be163ba..c3c136f 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -417,8 +417,8 @@
     });
 }
 
-void RenderProxy::setForceDark(bool enable) {
-    mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
+void RenderProxy::setForceDark(ForceDarkType type) {
+    mRenderThread.queue().post([this, type]() { mContext->setForceDark(type); });
 }
 
 void RenderProxy::copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 47c1b0c..f2d8e94 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -31,6 +31,7 @@
 #include "DrawFrameTask.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
+#include "utils/ForceDark.h"
 
 class SkBitmap;
 class SkPicture;
@@ -142,7 +143,7 @@
 
     void addFrameMetricsObserver(FrameMetricsObserver* observer);
     void removeFrameMetricsObserver(FrameMetricsObserver* observer);
-    void setForceDark(bool enable);
+    void setForceDark(ForceDarkType type);
 
     static void copySurfaceInto(ANativeWindow* window, std::shared_ptr<CopyRequest>&& request);
     static void prepareToDraw(Bitmap& bitmap);
diff --git a/libs/hwui/utils/ForceDark.h b/libs/hwui/utils/ForceDark.h
new file mode 100644
index 0000000..28538c4b
--- /dev/null
+++ b/libs/hwui/utils/ForceDark.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#ifndef FORCEDARKUTILS_H
+#define FORCEDARKUTILS_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * The type of force dark set on the renderer, if any.
+ *
+ * This should stay in sync with the java @IntDef in
+ * frameworks/base/graphics/java/android/graphics/ForceDarkType.java
+ */
+enum class ForceDarkType : __uint8_t { NONE = 0, FORCE_DARK = 1, FORCE_INVERT_COLOR_DARK = 2 };
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif  // FORCEDARKUTILS_H
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c883b1f2..2a89a99 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -35,6 +35,7 @@
         "androidx.compose.ui_ui",
         "androidx.compose.ui_ui-tooling",
         "androidx.core_core-ktx",
+        "androidx.hilt_hilt-navigation-compose",
         "androidx.lifecycle_lifecycle-extensions",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 0a63cb7..f2df64a 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -21,16 +21,10 @@
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.wear.compose.material.MaterialTheme
 import com.android.credentialmanager.ui.WearApp
-import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
-import com.google.android.horologist.compose.layout.belowTimeTextPreview
 import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
 
 @AndroidEntryPoint(ComponentActivity::class)
 class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() {
@@ -42,50 +36,14 @@
         super.onCreate(savedInstanceState)
 
         setTheme(android.R.style.Theme_DeviceDefault)
-
-        // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
-        // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
-        // contents of the "if" bracket.
-        if (false) {
-            setContent {
-                MaterialTheme {
-                    WearApp(
-                        viewModel = viewModel,
-                        onCloseApp = ::finish,
-                    )
-                }
-            }
-        } else {
-            // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
-            lifecycleScope.launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    viewModel.uiState.collect { uiState ->
-                        when (uiState) {
-                            CredentialSelectorUiState.Idle -> {
-                                // Don't display anything, assuming that there should be minimal latency
-                                // to parse the Credential Manager intent and define the state of the
-                                // app. If latency is big, then a "loading" screen should be displayed
-                                // to the user.
-                            }
-
-                            is CredentialSelectorUiState.Get -> {
-                                setContent {
-                                    MaterialTheme {
-                                        SinglePasswordScreen(
-                                            columnState = belowTimeTextPreview(),
-                                            onCloseApp = ::finish,
-                                        )
-                                    }
-                                }
-                            }
-
-                            else -> finish()
-                        }
-                    }
-                }
+        setContent {
+            MaterialTheme {
+                WearApp(
+                    viewModel = viewModel,
+                    onCloseApp = ::finish,
+                )
             }
         }
-
         viewModel.onNewIntent(intent)
     }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
index 81a0672..c28df3e8 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -27,8 +27,8 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.credentialmanager.R
 import com.android.credentialmanager.TAG
 import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
@@ -47,7 +47,7 @@
     columnState: ScalingLazyColumnState,
     onCloseApp: () -> Unit,
     modifier: Modifier = Modifier,
-    viewModel: SinglePasswordScreenViewModel = viewModel(),
+    viewModel: SinglePasswordScreenViewModel = hiltViewModel(),
 ) {
     viewModel.initialize()
 
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
index e914d5c..8386bc1 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/card/CardPageProvider.kt
@@ -18,6 +18,9 @@
 
 import android.os.Bundle
 import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.material.icons.outlined.PowerOff
+import androidx.compose.material.icons.outlined.Shield
 import androidx.compose.material.icons.outlined.WarningAmber
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.res.stringResource
@@ -29,13 +32,15 @@
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.gallery.R
 import com.android.settingslib.spa.widget.card.CardButton
+import com.android.settingslib.spa.widget.card.CardModel
 import com.android.settingslib.spa.widget.card.SettingsCard
+import com.android.settingslib.spa.widget.card.SettingsCollapsibleCard
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
 
 object CardPageProvider : SettingsPageProvider {
-    override val name = "ActionButton"
+    override val name = "CardPage"
 
     override fun getTitle(arguments: Bundle?) = TITLE
 
@@ -44,18 +49,21 @@
         RegularScaffold(title = TITLE) {
             SettingsCardWithIcon()
             SettingsCardWithoutIcon()
+            SampleSettingsCollapsibleCard()
         }
     }
 
     @Composable
     private fun SettingsCardWithIcon() {
         SettingsCard(
-            title = stringResource(R.string.sample_title),
-            text = stringResource(R.string.sample_text),
-            imageVector = Icons.Outlined.WarningAmber,
-            buttons = listOf(
-                CardButton(text = "Action") {},
-                CardButton(text = "Action", isMain = true) {},
+            CardModel(
+                title = stringResource(R.string.sample_title),
+                text = stringResource(R.string.sample_text),
+                imageVector = Icons.Outlined.WarningAmber,
+                buttons = listOf(
+                    CardButton(text = "Action") {},
+                    CardButton(text = "Action", isMain = true) {},
+                )
             )
         )
     }
@@ -63,10 +71,39 @@
     @Composable
     private fun SettingsCardWithoutIcon() {
         SettingsCard(
-            title = stringResource(R.string.sample_title),
-            text = stringResource(R.string.sample_text),
-            buttons = listOf(
-                CardButton(text = "Action") {},
+            CardModel(
+                title = stringResource(R.string.sample_title),
+                text = stringResource(R.string.sample_text),
+                buttons = listOf(
+                    CardButton(text = "Action") {},
+                ),
+            )
+        )
+    }
+
+    @Composable
+    fun SampleSettingsCollapsibleCard() {
+        SettingsCollapsibleCard(
+            title = "More alerts",
+            imageVector = Icons.Outlined.Error,
+            models = listOf(
+                CardModel(
+                    title = stringResource(R.string.sample_title),
+                    text = stringResource(R.string.sample_text),
+                    imageVector = Icons.Outlined.PowerOff,
+                    buttons = listOf(
+                        CardButton(text = "Action") {},
+                    )
+                ),
+                CardModel(
+                    title = stringResource(R.string.sample_title),
+                    text = stringResource(R.string.sample_text),
+                    imageVector = Icons.Outlined.Shield,
+                    buttons = listOf(
+                        CardButton(text = "Action") {},
+                        CardButton(text = "Main action", isMain = true) {},
+                    )
+                )
             )
         )
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt
new file mode 100644
index 0000000..d48e564
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/UiModePreviews.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.settingslib.spa.debug
+
+import android.content.res.Configuration
+import androidx.compose.ui.tooling.preview.Preview
+
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+annotation class UiModePreviews
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 4088ffd..47660bc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
 import androidx.compose.ui.unit.dp
 
 object SettingsDimension {
+    val paddingSmall = 4.dp
+
     val itemIconSize = 24.dp
     val itemIconContainerSize = 72.dp
     val itemPaddingStart = 24.dp
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
new file mode 100644
index 0000000..c113f43
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/CardModel.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.card
+
+import androidx.compose.ui.graphics.vector.ImageVector
+
+data class CardButton(
+    val text: String,
+    val isMain: Boolean = false,
+    val onClick: () -> Unit,
+)
+
+data class CardModel(
+    val title: String,
+    val text: String,
+    val imageVector: ImageVector? = null,
+    val buttons: List<CardButton> = emptyList(),
+)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
index 98873e0..10e2686 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCard.kt
@@ -16,9 +16,9 @@
 
 package com.android.settingslib.spa.widget.card
 
-import android.content.res.Configuration
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
@@ -37,26 +37,15 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.debug.UiModePreviews
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape.CornerExtraLarge
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsTitle
 
-data class CardButton(
-    val text: String,
-    val isMain: Boolean = false,
-    val onClick: () -> Unit,
-)
-
 @Composable
-fun SettingsCard(
-    title: String,
-    text: String,
-    imageVector: ImageVector? = null,
-    buttons: List<CardButton> = emptyList(),
-) {
+fun SettingsCard(content: @Composable ColumnScope.() -> Unit) {
     Card(
         shape = CornerExtraLarge,
         colors = CardDefaults.cardColors(
@@ -68,16 +57,27 @@
                 horizontal = SettingsDimension.itemPaddingEnd,
                 vertical = SettingsDimension.itemPaddingAround,
             ),
+        content = content,
+    )
+}
+
+@Composable
+fun SettingsCard(model: CardModel) {
+    SettingsCard {
+        SettingsCardImpl(model)
+    }
+}
+
+@Composable
+internal fun SettingsCardImpl(model: CardModel) {
+    Column(
+        modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
+        verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
     ) {
-        Column(
-            modifier = Modifier.padding(SettingsDimension.itemPaddingStart),
-            verticalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingAround)
-        ) {
-            CardIcon(imageVector)
-            SettingsTitle(title)
-            SettingsBody(text)
-            Buttons(buttons)
-        }
+        CardIcon(model.imageVector)
+        SettingsTitle(model.title)
+        SettingsBody(model.text)
+        Buttons(model.buttons)
     }
 }
 
@@ -136,28 +136,19 @@
     }
 }
 
-@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
-@Composable
-private fun SettingsCardPreviewLight() {
-    SettingsCardPreview()
-}
-
-@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
-@Composable
-private fun SettingsCardPreviewDark() {
-    SettingsCardPreview()
-}
-
+@UiModePreviews
 @Composable
 private fun SettingsCardPreview() {
     SettingsTheme {
         SettingsCard(
-            title = "Lorem ipsum",
-            text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
-            imageVector = Icons.Outlined.WarningAmber,
-            buttons = listOf(
-                CardButton(text = "Action") {},
-                CardButton(text = "Action", isMain = true) {},
+            CardModel(
+                title = "Lorem ipsum",
+                text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+                imageVector = Icons.Outlined.WarningAmber,
+                buttons = listOf(
+                    CardButton(text = "Action") {},
+                    CardButton(text = "Action", isMain = true) {},
+                )
             )
         )
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
new file mode 100644
index 0000000..7d10645
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCard.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.card
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.material.icons.outlined.PowerOff
+import androidx.compose.material.icons.outlined.Shield
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.debug.UiModePreviews
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.ui.ExpandIcon
+import com.android.settingslib.spa.widget.ui.SettingsDialogItem
+import com.android.settingslib.spa.widget.ui.SettingsTitleSmall
+
+@Composable
+fun SettingsCollapsibleCard(
+    title: String,
+    imageVector: ImageVector,
+    models: List<CardModel>
+) {
+    var expanded by rememberSaveable { mutableStateOf(false) }
+    SettingsCard {
+        Header(title, imageVector, models.size, expanded) { expanded = it }
+        AnimatedVisibility(expanded) {
+            Column {
+                for (model in models) {
+                    HorizontalDivider(
+                        thickness = SettingsDimension.paddingSmall,
+                        color = MaterialTheme.colorScheme.surface,
+                    )
+                    SettingsCardImpl(model)
+                }
+            }
+        }
+    }
+}
+
+@Composable
+private fun Header(
+    title: String,
+    imageVector: ImageVector,
+    cardCount: Int,
+    expanded: Boolean,
+    setExpanded: (Boolean) -> Unit,
+) {
+    Row(
+        modifier = Modifier
+            .fillMaxWidth()
+            .clickable { setExpanded(!expanded) }
+            .padding(
+                horizontal = SettingsDimension.itemPaddingStart,
+                vertical = SettingsDimension.itemPaddingVertical,
+            ),
+        horizontalArrangement = Arrangement.spacedBy(SettingsDimension.itemPaddingStart),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        Icon(
+            imageVector = imageVector,
+            contentDescription = null,
+            modifier = Modifier.size(SettingsDimension.itemIconSize),
+            tint = MaterialTheme.colorScheme.primary,
+        )
+        Box(modifier = Modifier.weight(1f)) {
+            SettingsTitleSmall(title, useMediumWeight = true)
+        }
+        CardCount(cardCount, expanded)
+    }
+}
+
+@Composable
+private fun CardCount(modelSize: Int, expanded: Boolean) {
+    Surface(
+        shape = SettingsShape.CornerExtraLarge,
+        color = MaterialTheme.colorScheme.secondaryContainer,
+    ) {
+        Row(
+            modifier = Modifier.padding(SettingsDimension.paddingSmall),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Spacer(modifier = Modifier.padding(SettingsDimension.paddingSmall))
+            SettingsDialogItem(modelSize.toString())
+            ExpandIcon(expanded)
+        }
+    }
+}
+
+@UiModePreviews
+@Composable
+private fun SettingsCollapsibleCardPreview() {
+    SettingsTheme {
+        SettingsCollapsibleCard(
+            title = "More alerts",
+            imageVector = Icons.Outlined.Error,
+            models = listOf(
+                CardModel(
+                    title = "Lorem ipsum",
+                    text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+                    imageVector = Icons.Outlined.PowerOff,
+                    buttons = listOf(
+                        CardButton(text = "Action") {},
+                    )
+                ),
+                CardModel(
+                    title = "Lorem ipsum",
+                    text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
+                    imageVector = Icons.Outlined.Shield,
+                    buttons = listOf(
+                        CardButton(text = "Action") {},
+                        CardButton(text = "Main action", isMain = true) {},
+                    )
+                )
+            )
+        )
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index 8cbf7cc..a9974dc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -76,13 +76,7 @@
             contentPadding = contentPadding,
         ) {
             SpinnerText(options.find { it.id == selectedId })
-            Icon(
-                imageVector = when {
-                    expanded -> Icons.Outlined.ExpandLess
-                    else -> Icons.Outlined.ExpandMore
-                },
-                contentDescription = null,
-            )
+            ExpandIcon(expanded)
         }
         DropdownMenu(
             expanded = expanded,
@@ -110,6 +104,17 @@
 }
 
 @Composable
+internal fun ExpandIcon(expanded: Boolean) {
+    Icon(
+        imageVector = when {
+            expanded -> Icons.Outlined.ExpandLess
+            else -> Icons.Outlined.ExpandMore
+        },
+        contentDescription = null,
+    )
+}
+
+@Composable
 private fun SpinnerText(
     option: SpinnerOption?,
     modifier: Modifier = Modifier,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 7f1acff..f4b2843 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
@@ -39,12 +40,16 @@
     Text(
         text = title,
         color = MaterialTheme.colorScheme.onSurface,
-        style = MaterialTheme.typography.titleMedium.let {
-            when (useMediumWeight) {
-                true -> it.toMediumWeight()
-                else -> it
-            }
-        },
+        style = MaterialTheme.typography.titleMedium.withWeight(useMediumWeight),
+    )
+}
+
+@Composable
+fun SettingsTitleSmall(title: String, useMediumWeight: Boolean = false) {
+    Text(
+        text = title,
+        color = MaterialTheme.colorScheme.onSurface,
+        style = MaterialTheme.typography.titleSmall.withWeight(useMediumWeight),
     )
 }
 
@@ -78,7 +83,9 @@
 @Composable
 fun PlaceholderTitle(title: String) {
     Box(
-        modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPadding),
+        modifier = Modifier
+            .fillMaxSize()
+            .padding(SettingsDimension.itemPadding),
         contentAlignment = Alignment.Center,
     ) {
         Text(
@@ -89,6 +96,11 @@
     }
 }
 
+private fun TextStyle.withWeight(useMediumWeight: Boolean = false) = when (useMediumWeight) {
+    true -> toMediumWeight()
+    else -> this
+}
+
 @Preview
 @Composable
 private fun BasePreferencePreview() {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
index 0ec8507..fd3ae49 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCardTest.kt
@@ -35,8 +35,10 @@
     fun settingsCard_titleDisplayed() {
         composeTestRule.setContent {
             SettingsCard(
-                title = TITLE,
-                text = "",
+                CardModel(
+                    title = TITLE,
+                    text = "",
+                )
             )
         }
 
@@ -47,8 +49,10 @@
     fun settingsCard_textDisplayed() {
         composeTestRule.setContent {
             SettingsCard(
-                title = "",
-                text = TEXT,
+                CardModel(
+                    title = "",
+                    text = TEXT,
+                )
             )
         }
 
@@ -59,11 +63,13 @@
     fun settingsCard_buttonDisplayed() {
         composeTestRule.setContent {
             SettingsCard(
-                title = "",
-                text = "",
-                buttons = listOf(
-                    CardButton(text = TEXT) {}
-                ),
+                CardModel(
+                    title = "",
+                    text = "",
+                    buttons = listOf(
+                        CardButton(text = TEXT) {}
+                    ),
+                )
             )
         }
 
@@ -75,11 +81,13 @@
         var buttonClicked = false
         composeTestRule.setContent {
             SettingsCard(
-                title = "",
-                text = "",
-                buttons = listOf(
-                    CardButton(text = TEXT) { buttonClicked = true }
-                ),
+                CardModel(
+                    title = "",
+                    text = "",
+                    buttons = listOf(
+                        CardButton(text = TEXT) { buttonClicked = true }
+                    ),
+                )
             )
         }
 
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
new file mode 100644
index 0000000..efe1c70
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/card/SettingsCollapsibleCardTest.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.settingslib.spa.widget.card
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Error
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsCollapsibleCardTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun settingsCollapsibleCard_titleDisplayed() {
+        setContent()
+
+        composeTestRule.onNodeWithText(TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun settingsCollapsibleCard_cardCountDisplayed() {
+        setContent()
+
+        composeTestRule.onNodeWithText("1").assertIsDisplayed()
+    }
+
+    @Test
+    fun settingsCollapsibleCard_initial_cardTextNotExists() {
+        setContent()
+
+        composeTestRule.onNodeWithText(CARD_TEXT).assertDoesNotExist()
+    }
+
+    @Test
+    fun settingsCollapsibleCard_afterExpand_cardTextDisplayed() {
+        setContent()
+
+        composeTestRule.onNodeWithText(TITLE).performClick()
+
+        composeTestRule.onNodeWithText(CARD_TEXT).assertIsDisplayed()
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            SettingsCollapsibleCard(
+                title = TITLE,
+                imageVector = Icons.Outlined.Error,
+                models = listOf(
+                    CardModel(
+                        title = "",
+                        text = CARD_TEXT,
+                    )
+                ),
+            )
+        }
+    }
+
+    private companion object {
+        const val TITLE = "Title"
+        const val CARD_TEXT = "Card Text"
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index a93524f..51164e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -126,13 +126,25 @@
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
+        return getDevicesByStates(new int[] {
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_CONNECTING,
+                BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    public List<BluetoothDevice> getConnectableDevices() {
+        return getDevicesByStates(new int[] {
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_CONNECTING,
+                BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    private List<BluetoothDevice> getDevicesByStates(int[] states) {
         if (mService == null) {
-            return new ArrayList<BluetoothDevice>(0);
+            return new ArrayList<>(0);
         }
-        return mService.getDevicesMatchingConnectionStates(
-              new int[] {BluetoothProfile.STATE_CONNECTED,
-                         BluetoothProfile.STATE_CONNECTING,
-                         BluetoothProfile.STATE_DISCONNECTING});
+        return mService.getDevicesMatchingConnectionStates(states);
     }
 
     /*
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0e7b79b..119aef6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -136,7 +136,7 @@
     /**
      * create profile instance according to bluetooth supported profile list
      */
-    void updateLocalProfiles() {
+    synchronized void updateLocalProfiles() {
         List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles();
         if (CollectionUtils.isEmpty(supportedList)) {
             if (DEBUG) Log.d(TAG, "supportedList is null");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 6bc2716..6ff36d4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -29,6 +29,7 @@
 import android.content.AttributionSource;
 import android.content.IContentProvider;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -219,14 +220,33 @@
         return lines;
       }
 
+      private static void log(String msg) {
+        if (Build.IS_DEBUGGABLE) {
+            Slog.wtf(TAG, msg);
+        } else {
+            Slog.e(TAG, msg);
+        }
+      }
+
       public static List<String> listAllAconfigFlags(IContentProvider provider) {
         HashMap<String, String> allFlags = getAllFlags(provider);
         HashSet<String> aconfigFlagNames = getAconfigFlagNamesInDeviceConfig();
         final ArrayList<String> lines = new ArrayList<>();
-        for (String key : aconfigFlagNames) {
-          String val = allFlags.get(key);
+        for (String aconfigFlag : aconfigFlagNames) {
+          String val = allFlags.get(aconfigFlag);
           if (val != null) {
-            lines.add(key + "=" + val);
+            // aconfigFlag is in the form of [namespace]/[package].[flag_name]
+            int idx = aconfigFlag.indexOf("/");
+            if (idx == -1 || idx == aconfigFlag.length() - 1 || idx == 0) {
+              log("invalid flag entry in device config: " + aconfigFlag);
+              continue;
+            }
+
+            // we intend to print out [package].[flag_name] [namespace]=val
+            String aconfigFlagNameByPackage = aconfigFlag.substring(idx + 1);
+            String namespace = aconfigFlag.substring(0, idx);
+            lines.add("flag:" + aconfigFlagNameByPackage + " namespace:" + namespace +
+                " value:" + val);
           }
         }
         Collections.sort(lines);
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 56970d7..d668c69 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -154,7 +154,7 @@
                 Bouncer(
                     viewModel = viewModel,
                     dialogFactory = dialogFactory,
-                    isUserInputAreaVisible = true,
+                    userInputAreaVisibility = UserInputAreaVisibility.FULL,
                     modifier = childModifier,
                 )
             Layout.SIDE_BY_SIDE ->
@@ -189,7 +189,7 @@
 private fun Bouncer(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerSceneDialogFactory,
-    isUserInputAreaVisible: Boolean,
+    userInputAreaVisibility: UserInputAreaVisibility,
     modifier: Modifier = Modifier,
 ) {
     val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
@@ -214,12 +214,11 @@
         }
 
         Box(Modifier.weight(1f)) {
-            if (isUserInputAreaVisible) {
-                UserInputArea(
-                    viewModel = viewModel,
-                    modifier = Modifier.align(Alignment.Center),
-                )
-            }
+            UserInputArea(
+                viewModel = viewModel,
+                visibility = userInputAreaVisibility,
+                modifier = Modifier.align(Alignment.Center),
+            )
         }
 
         if (viewModel.isEmergencyButtonVisible) {
@@ -269,6 +268,7 @@
 @Composable
 private fun UserInputArea(
     viewModel: BouncerViewModel,
+    visibility: UserInputAreaVisibility,
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
@@ -276,21 +276,46 @@
 
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
-            PinBouncer(
-                viewModel = nonNullViewModel,
-                modifier = modifier,
-            )
+            when (visibility) {
+                UserInputAreaVisibility.FULL ->
+                    PinBouncer(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+                UserInputAreaVisibility.INPUT_ONLY ->
+                    PinPad(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+                UserInputAreaVisibility.OUTPUT_ONLY ->
+                    PinInputDisplay(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+                UserInputAreaVisibility.NONE -> {}
+            }
         is PasswordBouncerViewModel ->
-            PasswordBouncer(
-                viewModel = nonNullViewModel,
-                modifier = modifier,
-            )
+            when (visibility) {
+                UserInputAreaVisibility.FULL,
+                UserInputAreaVisibility.INPUT_ONLY ->
+                    PasswordBouncer(
+                        viewModel = nonNullViewModel,
+                        modifier = modifier,
+                    )
+                else -> {}
+            }
         is PatternBouncerViewModel ->
-            PatternBouncer(
-                viewModel = nonNullViewModel,
-                modifier =
-                    Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
-            )
+            when (visibility) {
+                UserInputAreaVisibility.FULL,
+                UserInputAreaVisibility.INPUT_ONLY ->
+                    PatternBouncer(
+                        viewModel = nonNullViewModel,
+                        modifier =
+                            Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+                                .then(modifier)
+                    )
+                else -> {}
+            }
         else -> Unit
     }
 }
@@ -435,13 +460,14 @@
             Bouncer(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
-                isUserInputAreaVisible = false,
+                userInputAreaVisibility = UserInputAreaVisibility.OUTPUT_ONLY,
                 modifier = startContentModifier,
             )
         },
         endContent = { endContentModifier ->
             UserInputArea(
                 viewModel = viewModel,
+                visibility = UserInputAreaVisibility.INPUT_ONLY,
                 modifier = endContentModifier,
             )
         },
@@ -545,7 +571,7 @@
             Bouncer(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
-                isUserInputAreaVisible = true,
+                userInputAreaVisibility = UserInputAreaVisibility.FULL,
                 modifier = endContentModifier,
             )
         },
@@ -574,7 +600,7 @@
         Bouncer(
             viewModel = viewModel,
             dialogFactory = dialogFactory,
-            isUserInputAreaVisible = true,
+            userInputAreaVisibility = UserInputAreaVisibility.FULL,
             modifier = Modifier.fillMaxWidth().weight(1f),
         )
     }
@@ -630,6 +656,27 @@
     SPLIT,
 }
 
+/** Enumerates all supported user-input area visibilities. */
+private enum class UserInputAreaVisibility {
+    /**
+     * The entire user input area is shown, including where the user enters input and where it's
+     * reflected to the user.
+     */
+    FULL,
+    /**
+     * Only the area where the user enters the input is shown; the area where the input is reflected
+     * back to the user is not shown.
+     */
+    INPUT_ONLY,
+    /**
+     * Only the area where the input is reflected back to the user is shown; the area where the
+     * input is entered by the user is not shown.
+     */
+    OUTPUT_ONLY,
+    /** The entire user input area is hidden. */
+    NONE,
+}
+
 /**
  * Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
  * the two reaches a stopping point but `0` in the middle of the transition.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 6491b70..84e0167 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -55,12 +55,12 @@
 import com.android.compose.animation.Easings
 import com.android.compose.grid.VerticalGrid
 import com.android.compose.modifiers.thenIf
-import com.android.systemui.res.R
 import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.res.R
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.DurationUnit
 import kotlinx.coroutines.async
@@ -93,7 +93,10 @@
 }
 
 @Composable
-private fun PinPad(viewModel: PinBouncerViewModel) {
+fun PinPad(
+    viewModel: PinBouncerViewModel,
+    modifier: Modifier = Modifier,
+) {
     val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
     val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
     val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
@@ -112,6 +115,7 @@
         columns = 3,
         verticalSpacing = 12.dp,
         horizontalSpacing = 20.dp,
+        modifier = modifier,
     ) {
         repeat(9) { index ->
             DigitButton(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 055ece3..814ea31 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -51,10 +51,10 @@
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.Easings
 import com.android.keyguard.PinShapeAdapter
-import com.android.systemui.res.R
 import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PinInputViewModel
+import com.android.systemui.res.R
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.async
@@ -65,7 +65,10 @@
 import kotlinx.coroutines.launch
 
 @Composable
-fun PinInputDisplay(viewModel: PinBouncerViewModel) {
+fun PinInputDisplay(
+    viewModel: PinBouncerViewModel,
+    modifier: Modifier = Modifier,
+) {
     val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
     val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
 
@@ -81,8 +84,8 @@
     // unifying into a single, more complex implementation.
 
     when (val length = hintedPinLength) {
-        null -> RegularPinInputDisplay(viewModel, shapeAnimations)
-        else -> HintingPinInputDisplay(viewModel, shapeAnimations, length)
+        null -> RegularPinInputDisplay(viewModel, shapeAnimations, modifier)
+        else -> HintingPinInputDisplay(viewModel, shapeAnimations, length, modifier)
     }
 }
 
@@ -97,6 +100,7 @@
     viewModel: PinBouncerViewModel,
     shapeAnimations: ShapeAnimations,
     hintedPinLength: Int,
+    modifier: Modifier = Modifier,
 ) {
     val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
     // [ClearAll] marker pointing at the beginning of the current pin input.
@@ -151,7 +155,7 @@
     LaunchedEffect(Unit) { playAnimation = true }
 
     val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
-    Row(modifier = Modifier.heightIn(min = shapeAnimations.shapeSize)) {
+    Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) {
         pinEntryDrawable.forEachIndexed { index, drawable ->
             // Key the loop by [index] and [drawable], so that updating a shape drawable at the same
             // index will play the new animation (by remembering a new [atEnd]).
@@ -183,6 +187,7 @@
 private fun RegularPinInputDisplay(
     viewModel: PinBouncerViewModel,
     shapeAnimations: ShapeAnimations,
+    modifier: Modifier = Modifier,
 ) {
     // Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from
     // `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove
@@ -226,7 +231,7 @@
             }
     }
 
-    pinInputRow.Content()
+    pinInputRow.Content(modifier)
 }
 
 private class PinInputRow(
@@ -235,10 +240,11 @@
     private val entries = mutableStateListOf<PinInputEntry>()
 
     @Composable
-    fun Content() {
+    fun Content(modifier: Modifier) {
         Row(
             modifier =
-                Modifier.heightIn(min = shapeAnimations.shapeSize)
+                modifier
+                    .heightIn(min = shapeAnimations.shapeSize)
                     // Pins overflowing horizontally should still be shown as scrolling.
                     .wrapContentSize(unbounded = true),
         ) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 17726ab..77b844d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -16,15 +16,16 @@
 
 package com.android.systemui.communal.ui.compose
 
-import android.appwidget.AppWidgetHostView
 import android.os.Bundle
 import android.util.SizeF
+import android.widget.FrameLayout
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
@@ -44,8 +45,8 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
+import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalContentSize
-import com.android.systemui.communal.ui.model.CommunalContentUiModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.res.R
 
@@ -54,8 +55,7 @@
     modifier: Modifier = Modifier,
     viewModel: CommunalViewModel,
 ) {
-    val showTutorial by viewModel.showTutorialContent.collectAsState(initial = false)
-    val widgetContent by viewModel.widgetContent.collectAsState(initial = emptyList())
+    val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
     Box(
         modifier = modifier.fillMaxSize().background(Color.White),
     ) {
@@ -65,31 +65,21 @@
             horizontalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
             verticalArrangement = Arrangement.spacedBy(Dimensions.Spacing),
         ) {
-            if (showTutorial) {
-                items(
-                    count = tutorialContentSizes.size,
-                    // TODO(b/308148193): a more scalable solution for unique ids.
-                    key = { index -> "tutorial_$index" },
-                    span = { index -> GridItemSpan(tutorialContentSizes[index].span) },
-                ) { index ->
-                    TutorialCard(
-                        modifier =
-                            Modifier.size(Dimensions.CardWidth, tutorialContentSizes[index].dp()),
-                    )
-                }
-            } else {
-                items(
-                    count = widgetContent.size,
-                    key = { index -> widgetContent[index].id },
-                    span = { index -> GridItemSpan(widgetContent[index].size.span) },
-                ) { index ->
-                    val widget = widgetContent[index]
-                    ContentCard(
-                        modifier = Modifier.size(Dimensions.CardWidth, widget.size.dp()),
-                        model = widget,
-                        deleteOnClick = viewModel::onDeleteWidget
-                    )
-                }
+            items(
+                count = communalContent.size,
+                key = { index -> communalContent[index].key },
+                span = { index -> GridItemSpan(communalContent[index].size.span) },
+            ) { index ->
+                CommunalContent(
+                    modifier = Modifier.fillMaxHeight().width(Dimensions.CardWidth),
+                    model = communalContent[index],
+                    deleteOnClick = viewModel::onDeleteWidget,
+                    size =
+                        SizeF(
+                            Dimensions.CardWidth.value,
+                            communalContent[index].size.dp().value,
+                        ),
+                )
             }
         }
         IconButton(onClick = viewModel::onOpenWidgetPicker) {
@@ -101,15 +91,24 @@
     }
 }
 
-// A placeholder for tutorial content.
 @Composable
-private fun TutorialCard(modifier: Modifier = Modifier) {
-    Card(modifier = modifier, content = {})
+private fun CommunalContent(
+    model: CommunalContentModel,
+    size: SizeF,
+    deleteOnClick: (id: Int) -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    when (model) {
+        is CommunalContentModel.Widget -> WidgetContent(model, size, deleteOnClick, modifier)
+        is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
+        is CommunalContentModel.Tutorial -> TutorialContent(modifier)
+    }
 }
 
 @Composable
-private fun ContentCard(
-    model: CommunalContentUiModel,
+private fun WidgetContent(
+    model: CommunalContentModel.Widget,
+    size: SizeF,
     deleteOnClick: (id: Int) -> Unit,
     modifier: Modifier = Modifier,
 ) {
@@ -117,31 +116,45 @@
     Box(
         modifier = modifier.fillMaxSize().background(Color.White),
     ) {
-        // TODO(b/308148193): this will be cleaned up soon once the change to convert to
-        // CommunalContentUiModel interface is merged
-        val widgetId = getWidgetId(model.id)
-        widgetId?.let {
-            IconButton(onClick = { deleteOnClick(it) }) {
-                Icon(
-                    Icons.Default.Close,
-                    LocalContext.current.getString(R.string.button_to_remove_widget)
-                )
-            }
+        IconButton(onClick = { deleteOnClick(model.appWidgetId) }) {
+            Icon(
+                Icons.Default.Close,
+                LocalContext.current.getString(R.string.button_to_remove_widget)
+            )
         }
         AndroidView(
             modifier = modifier,
-            factory = {
-                model.view.apply {
-                    if (this is AppWidgetHostView) {
-                        val size = SizeF(Dimensions.CardWidth.value, model.size.dp().value)
-                        updateAppWidgetSize(Bundle.EMPTY, listOf(size))
-                    }
-                }
+            factory = { context ->
+                model.appWidgetHost
+                    .createView(context, model.appWidgetId, model.providerInfo)
+                    .apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
             },
+            // For reusing composition in lazy lists.
+            onReset = {}
         )
     }
 }
 
+@Composable
+private fun SmartspaceContent(
+    model: CommunalContentModel.Smartspace,
+    modifier: Modifier = Modifier,
+) {
+    AndroidView(
+        modifier = modifier,
+        factory = { context ->
+            FrameLayout(context).apply { addView(model.remoteViews.apply(context, this)) }
+        },
+        // For reusing composition in lazy lists.
+        onReset = {}
+    )
+}
+
+@Composable
+private fun TutorialContent(modifier: Modifier = Modifier) {
+    Card(modifier = modifier, content = {})
+}
+
 private fun CommunalContentSize.dp(): Dp {
     return when (this) {
         CommunalContentSize.FULL -> Dimensions.CardHeightFull
@@ -150,23 +163,6 @@
     }
 }
 
-private fun getWidgetId(id: String): Int? {
-    return if (id.startsWith("widget_")) id.substring("widget_".length).toInt() else null
-}
-
-// Sizes for the tutorial placeholders.
-private val tutorialContentSizes =
-    listOf(
-        CommunalContentSize.FULL,
-        CommunalContentSize.THIRD,
-        CommunalContentSize.THIRD,
-        CommunalContentSize.THIRD,
-        CommunalContentSize.HALF,
-        CommunalContentSize.HALF,
-        CommunalContentSize.HALF,
-        CommunalContentSize.HALF,
-    )
-
 private object Dimensions {
     val CardWidth = 464.dp
     val CardHeightFull = 630.dp
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
new file mode 100644
index 0000000..7196de6
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlagsClassic
+import com.android.systemui.flags.Flags
+import com.android.systemui.scene.SceneTestUtils
+import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalRepositoryImplTest : SysuiTestCase() {
+    private lateinit var underTest: CommunalRepositoryImpl
+
+    private lateinit var testScope: TestScope
+
+    private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
+    private lateinit var sceneContainerFlags: FakeSceneContainerFlags
+    private lateinit var sceneContainerRepository: SceneContainerRepository
+
+    @Before
+    fun setUp() {
+        testScope = TestScope()
+
+        val sceneTestUtils = SceneTestUtils(this)
+        sceneContainerFlags = FakeSceneContainerFlags(enabled = false)
+        sceneContainerRepository = sceneTestUtils.fakeSceneContainerRepository()
+        featureFlagsClassic = FakeFeatureFlagsClassic()
+
+        featureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
+
+        underTest =
+            CommunalRepositoryImpl(
+                featureFlagsClassic,
+                sceneContainerFlags,
+                sceneContainerRepository,
+            )
+    }
+
+    @Test
+    fun isCommunalShowing_sceneContainerDisabled_onCommunalScene_true() =
+        testScope.runTest {
+            underTest.setDesiredScene(CommunalSceneKey.Communal)
+
+            val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+            assertThat(isCommunalHubShowing).isTrue()
+        }
+
+    @Test
+    fun isCommunalShowing_sceneContainerDisabled_onBlankScene_false() =
+        testScope.runTest {
+            underTest.setDesiredScene(CommunalSceneKey.Blank)
+
+            val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+            assertThat(isCommunalHubShowing).isFalse()
+        }
+
+    @Test
+    fun isCommunalShowing_sceneContainerEnabled_onCommunalScene_true() =
+        testScope.runTest {
+            sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+            underTest =
+                CommunalRepositoryImpl(
+                    featureFlagsClassic,
+                    sceneContainerFlags,
+                    sceneContainerRepository,
+                )
+
+            sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Communal))
+
+            val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+            assertThat(isCommunalHubShowing).isTrue()
+        }
+
+    @Test
+    fun isCommunalShowing_sceneContainerEnabled_onLockscreenScene_false() =
+        testScope.runTest {
+            sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
+            underTest =
+                CommunalRepositoryImpl(
+                    featureFlagsClassic,
+                    sceneContainerFlags,
+                    sceneContainerRepository,
+                )
+
+            sceneContainerRepository.setDesiredScene(SceneModel(key = SceneKey.Lockscreen))
+
+            val isCommunalHubShowing by collectLastValue(underTest.isCommunalHubShowing)
+            assertThat(isCommunalHubShowing).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
new file mode 100644
index 0000000..9a3129f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalTutorialInteractorTest : SysuiTestCase() {
+
+    @Mock private lateinit var userTracker: UserTracker
+
+    private lateinit var testScope: TestScope
+    private lateinit var underTest: CommunalTutorialInteractor
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
+    private lateinit var communalRepository: FakeCommunalRepository
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        testScope = TestScope()
+
+        val withDeps = CommunalTutorialInteractorFactory.create(testScope)
+        keyguardRepository = withDeps.keyguardRepository
+        communalTutorialRepository = withDeps.communalTutorialRepository
+        communalRepository = withDeps.communalRepository
+
+        underTest = withDeps.communalTutorialInteractor
+
+        whenever(userTracker.userHandle).thenReturn(mock())
+    }
+
+    @Test
+    fun tutorialUnavailable_whenKeyguardNotVisible() =
+        testScope.runTest {
+            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+            keyguardRepository.setKeyguardShowing(false)
+            assertThat(isTutorialAvailable).isFalse()
+        }
+
+    @Test
+    fun tutorialUnavailable_whenTutorialIsCompleted() =
+        testScope.runTest {
+            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            communalRepository.setIsCommunalHubShowing(false)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+            assertThat(isTutorialAvailable).isFalse()
+        }
+
+    @Test
+    fun tutorialAvailable_whenTutorialNotStarted() =
+        testScope.runTest {
+            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            communalRepository.setIsCommunalHubShowing(false)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+            assertThat(isTutorialAvailable).isTrue()
+        }
+
+    @Test
+    fun tutorialAvailable_whenTutorialIsStarted() =
+        testScope.runTest {
+            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            communalRepository.setIsCommunalHubShowing(true)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+            assertThat(isTutorialAvailable).isTrue()
+        }
+
+    @Test
+    fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            communalRepository.setIsCommunalHubShowing(true)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            communalRepository.setIsCommunalHubShowing(true)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
+        }
+
+    @Test
+    fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            communalRepository.setIsCommunalHubShowing(true)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
+
+            communalRepository.setIsCommunalHubShowing(false)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
+        }
+
+    @Test
+    fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalRepository.setIsCommunalHubShowing(true)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
+
+            communalRepository.setIsCommunalHubShowing(false)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+
+    @Test
+    fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
+        testScope.runTest {
+            val tutorialSettingState by
+                collectLastValue(communalTutorialRepository.tutorialSettingState)
+            communalRepository.setIsCommunalHubShowing(true)
+            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            communalRepository.setIsCommunalHubShowing(false)
+
+            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index b40570b..3119b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -21,16 +21,24 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlags
+import com.android.systemui.scene.shared.model.SceneKey
 import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
 
 /** Encapsulates the state of communal mode. */
 interface CommunalRepository {
     /** Whether communal features are enabled. */
     val isCommunalEnabled: Boolean
 
+    /** Whether the communal hub is showing. */
+    val isCommunalHubShowing: Flow<Boolean>
+
     /**
      * Target scene as requested by the underlying [SceneTransitionLayout] or through
      * [setDesiredScene].
@@ -46,6 +54,8 @@
 @Inject
 constructor(
     private val featureFlagsClassic: FeatureFlagsClassic,
+    sceneContainerFlags: SceneContainerFlags,
+    sceneContainerRepository: SceneContainerRepository,
 ) : CommunalRepository {
     override val isCommunalEnabled: Boolean
         get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
@@ -57,4 +67,11 @@
     override fun setDesiredScene(desiredScene: CommunalSceneKey) {
         _desiredScene.value = desiredScene
     }
+
+    override val isCommunalHubShowing: Flow<Boolean> =
+        if (sceneContainerFlags.isEnabled()) {
+            sceneContainerRepository.desiredScene.map { scene -> scene.key == SceneKey.Communal }
+        } else {
+            desiredScene.map { sceneKey -> sceneKey == CommunalSceneKey.Communal }
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 2c683ee..524cccf 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -16,16 +16,24 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import android.app.smartspace.SmartspaceTarget
+import android.appwidget.AppWidgetHost
 import android.content.ComponentName
 import com.android.systemui.communal.data.repository.CommunalRepository
 import com.android.systemui.communal.data.repository.CommunalWidgetRepository
+import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
+import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.smartspace.data.repository.SmartspaceRepository
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 
 /** Encapsulates business-logic related to communal mode. */
@@ -35,6 +43,9 @@
 constructor(
     private val communalRepository: CommunalRepository,
     private val widgetRepository: CommunalWidgetRepository,
+    smartspaceRepository: SmartspaceRepository,
+    tutorialInteractor: CommunalTutorialInteractor,
+    private val appWidgetHost: AppWidgetHost,
 ) {
 
     /** Whether communal features are enabled. */
@@ -45,14 +56,6 @@
     val appWidgetInfo: Flow<CommunalAppWidgetInfo?> = widgetRepository.stopwatchAppWidgetInfo
 
     /**
-     * A flow of information about widgets to be shown in communal hub.
-     *
-     * Currently only showing persistent widgets that have been bound to the app widget service
-     * (have an allocated id).
-     */
-    val widgetContent: Flow<List<CommunalWidgetContentModel>> = widgetRepository.communalWidgets
-
-    /**
      * Target scene as requested by the underlying [SceneTransitionLayout] or through
      * [onSceneChanged].
      */
@@ -76,4 +79,63 @@
 
     /** Delete a widget by id. */
     fun deleteWidget(id: Int) = widgetRepository.deleteWidget(id)
+
+    /** A list of all the communal content to be displayed in the communal hub. */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val communalContent: Flow<List<CommunalContentModel>> =
+        tutorialInteractor.isTutorialAvailable.flatMapLatest { isTutorialMode ->
+            if (isTutorialMode) {
+                return@flatMapLatest flowOf(tutorialContent)
+            }
+            combine(smartspaceContent, widgetContent) { smartspace, widgets ->
+                smartspace + widgets
+            }
+        }
+
+    /** A list of widget content to be displayed in the communal hub. */
+    private val widgetContent: Flow<List<CommunalContentModel.Widget>> =
+        widgetRepository.communalWidgets.map { widgets ->
+            widgets.map Widget@{ widget ->
+                return@Widget CommunalContentModel.Widget(
+                    appWidgetId = widget.appWidgetId,
+                    providerInfo = widget.providerInfo,
+                    appWidgetHost = appWidgetHost,
+                )
+            }
+        }
+
+    /** A flow of available smartspace content. Currently only showing timer targets. */
+    private val smartspaceContent: Flow<List<CommunalContentModel.Smartspace>> =
+        if (!smartspaceRepository.isSmartspaceRemoteViewsEnabled) {
+            flowOf(emptyList())
+        } else {
+            smartspaceRepository.lockscreenSmartspaceTargets.map { targets ->
+                targets
+                    .filter { target ->
+                        target.featureType == SmartspaceTarget.FEATURE_TIMER &&
+                            target.remoteViews != null
+                    }
+                    .map Target@{ target ->
+                        return@Target CommunalContentModel.Smartspace(
+                            smartspaceTargetId = target.smartspaceTargetId,
+                            remoteViews = target.remoteViews!!,
+                            // Smartspace always as HALF for now.
+                            size = CommunalContentSize.HALF,
+                        )
+                    }
+            }
+        }
+
+    /** A list of tutorial content to be displayed in the communal hub in tutorial mode. */
+    private val tutorialContent: List<CommunalContentModel.Tutorial> =
+        listOf(
+            CommunalContentModel.Tutorial(id = 0, CommunalContentSize.FULL),
+            CommunalContentModel.Tutorial(id = 1, CommunalContentSize.THIRD),
+            CommunalContentModel.Tutorial(id = 2, CommunalContentSize.THIRD),
+            CommunalContentModel.Tutorial(id = 3, CommunalContentSize.THIRD),
+            CommunalContentModel.Tutorial(id = 4, CommunalContentSize.HALF),
+            CommunalContentModel.Tutorial(id = 5, CommunalContentSize.HALF),
+            CommunalContentModel.Tutorial(id = 6, CommunalContentSize.HALF),
+            CommunalContentModel.Tutorial(id = 7, CommunalContentSize.HALF),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 7f43eb5..5ca89f2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -17,13 +17,11 @@
 package com.android.systemui.communal.domain.interactor
 
 import android.provider.Settings
+import com.android.systemui.communal.data.repository.CommunalRepository
 import com.android.systemui.communal.data.repository.CommunalTutorialRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,9 +44,7 @@
     @Application private val scope: CoroutineScope,
     private val communalTutorialRepository: CommunalTutorialRepository,
     keyguardInteractor: KeyguardInteractor,
-    private val communalInteractor: CommunalInteractor,
-    private val sceneContainerFlags: SceneContainerFlags,
-    private val sceneInteractor: SceneInteractor,
+    private val communalRepository: CommunalRepository,
 ) {
     /** An observable for whether the tutorial is available. */
     val isTutorialAvailable: Flow<Boolean> =
@@ -74,17 +70,11 @@
                 if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
                     return@flatMapLatest flowOf(null)
                 }
-                if (sceneContainerFlags.isEnabled()) {
-                    sceneInteractor.desiredScene.map { sceneModel ->
-                        nextStateAfterTransition(
-                            tutorialSettingState,
-                            sceneModel.key == SceneKey.Communal
-                        )
-                    }
-                } else {
-                    communalInteractor.isCommunalShowing.map {
-                        nextStateAfterTransition(tutorialSettingState, it)
-                    }
+                communalRepository.isCommunalHubShowing.map { isCommunalShowing ->
+                    nextStateAfterTransition(
+                        tutorialSettingState,
+                        isCommunalShowing,
+                    )
                 }
             }
             .filterNotNull()
@@ -102,7 +92,7 @@
 
     private var job: Job? = null
     private fun listenForTransitionToUpdateTutorialState() {
-        if (!communalInteractor.isCommunalEnabled) {
+        if (!communalRepository.isCommunalEnabled) {
             return
         }
         job =
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
new file mode 100644
index 0000000..69382a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.model
+
+import android.appwidget.AppWidgetHost
+import android.appwidget.AppWidgetProviderInfo
+import android.widget.RemoteViews
+import com.android.systemui.communal.shared.model.CommunalContentSize
+
+/** Encapsulates data for a communal content. */
+sealed interface CommunalContentModel {
+    /** Unique key across all types of content models. */
+    val key: String
+
+    /** Size to be rendered in the grid. */
+    val size: CommunalContentSize
+
+    class Widget(
+        val appWidgetId: Int,
+        val providerInfo: AppWidgetProviderInfo,
+        val appWidgetHost: AppWidgetHost,
+    ) : CommunalContentModel {
+        override val key = "widget_$appWidgetId"
+        // Widget size is always half.
+        override val size = CommunalContentSize.HALF
+    }
+
+    class Tutorial(
+        id: Int,
+        override val size: CommunalContentSize,
+    ) : CommunalContentModel {
+        override val key = "tutorial_$id"
+    }
+
+    class Smartspace(
+        smartspaceTargetId: String,
+        val remoteViews: RemoteViews,
+        override val size: CommunalContentSize,
+    ) : CommunalContentModel {
+        override val key = "smartspace_$smartspaceTargetId"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
deleted file mode 100644
index b60dc2a..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/model/CommunalContentUiModel.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.systemui.communal.ui.model
-
-import android.view.View
-import com.android.systemui.communal.shared.model.CommunalContentSize
-
-/**
- * Encapsulates data for a communal content that holds a view.
- *
- * This model stays in the UI layer.
- */
-data class CommunalContentUiModel(
-    val id: String,
-    val view: View,
-    val size: CommunalContentSize = CommunalContentSize.HALF,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 197dece..3df6e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,53 +16,29 @@
 
 package com.android.systemui.communal.ui.viewmodel
 
-import android.appwidget.AppWidgetHost
 import android.content.ComponentName
-import android.content.Context
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
+import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.communal.ui.model.CommunalContentUiModel
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
 
 @SysUISingleton
 class CommunalViewModel
 @Inject
 constructor(
-    @Application private val context: Context,
-    private val appWidgetHost: AppWidgetHost,
     private val communalInteractor: CommunalInteractor,
-    tutorialInteractor: CommunalTutorialInteractor,
 ) {
-    /** Whether communal hub should show tutorial content. */
-    val showTutorialContent: Flow<Boolean> = tutorialInteractor.isTutorialAvailable
-
-    /** List of widgets to be displayed in the communal hub. */
-    val widgetContent: Flow<List<CommunalContentUiModel>> =
-        communalInteractor.widgetContent.map { widgets ->
-            widgets.map Widget@{ widget ->
-                // TODO(b/306406256): As adding and removing widgets functionalities are
-                // supported, cache the host views so they're not recreated each time.
-                val hostView =
-                    appWidgetHost.createView(context, widget.appWidgetId, widget.providerInfo)
-                return@Widget CommunalContentUiModel(
-                    // TODO(b/308148193): a more scalable solution for unique ids.
-                    id = "widget_${widget.appWidgetId}",
-                    view = hostView,
-                )
-            }
-        }
-
     val currentScene: StateFlow<CommunalSceneKey> = communalInteractor.desiredScene
     fun onSceneChanged(scene: CommunalSceneKey) {
         communalInteractor.onSceneChanged(scene)
     }
 
+    /** A list of all the communal content to be displayed in the communal hub. */
+    val communalContent: Flow<List<CommunalContentModel>> = communalInteractor.communalContent
+
     /** Delete a widget by id. */
     fun onDeleteWidget(id: Int) = communalInteractor.deleteWidget(id)
 
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index cf86885..20a9e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -20,15 +20,18 @@
 import android.companion.virtual.flags.Flags
 import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.display.data.repository.DisplayRepository
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 /** Provides information about an external connected display. */
@@ -81,6 +84,7 @@
     private val virtualDeviceManager: VirtualDeviceManager,
     keyguardRepository: KeyguardRepository,
     displayRepository: DisplayRepository,
+    @Background backgroundCoroutineDispatcher: CoroutineDispatcher,
 ) : ConnectedDisplayInteractor {
 
     override val connectedDisplayState: Flow<State> =
@@ -101,6 +105,7 @@
                     State.CONNECTED
                 }
             }
+            .flowOn(backgroundCoroutineDispatcher)
             .distinctUntilChanged()
 
     override val connectedDisplayAddition: Flow<Unit> =
@@ -108,6 +113,7 @@
             .filter {
                 it != null && (isExternalDisplay(it) || isVirtualDeviceOwnedMirrorDisplay(it))
             }
+            .flowOn(backgroundCoroutineDispatcher)
             .map {} // map to Unit
 
     // Provides the pending display only if the lockscreen is unlocked
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 8fed571..dd971b9 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -481,12 +481,6 @@
             namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
         )
 
-    // TODO(b/254512674): Tracking Bug
-    @Keep
-    @JvmField
-    val HIDE_NAVBAR_WINDOW =
-        sysPropBooleanFlag("persist.wm.debug.hide_navbar_window", default = false)
-
     @Keep
     @JvmField
     val WM_CAPTION_ON_SHELL =
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index eea369f..654fffe8 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.mediaprojection.permission
 
+import android.app.AlertDialog
 import android.content.Context
 import android.os.Bundle
 import android.view.Gravity
@@ -28,36 +29,36 @@
 import android.widget.ImageView
 import android.widget.Spinner
 import android.widget.TextView
+import androidx.annotation.CallSuper
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import androidx.annotation.LayoutRes
 import androidx.annotation.StringRes
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.DialogDelegate
 
 /** Base permission dialog for screen share and recording */
-open class BaseScreenSharePermissionDialog(
-    context: Context,
+abstract class BaseMediaProjectionPermissionDialogDelegate<T : AlertDialog>(
     private val screenShareOptions: List<ScreenShareOption>,
     private val appName: String?,
     private val hostUid: Int,
     private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     @DrawableRes private val dialogIconDrawable: Int? = null,
     @ColorRes private val dialogIconTint: Int? = null,
-) : SystemUIDialog(context), AdapterView.OnItemSelectedListener {
+) : DialogDelegate<T>, AdapterView.OnItemSelectedListener {
     private lateinit var dialogTitle: TextView
     private lateinit var startButton: TextView
     private lateinit var cancelButton: TextView
     private lateinit var warning: TextView
     private lateinit var screenShareModeSpinner: Spinner
     private var hasCancelBeenLogged: Boolean = false
+    protected lateinit var dialog: AlertDialog
     var selectedScreenShareOption: ScreenShareOption = screenShareOptions.first()
 
-    override fun dismiss() {
-        super.dismiss()
-
-        // Dismiss can be called multiple times and we only want to log once.
+    @CallSuper
+    override fun onStop(dialog: T) {
+        // onStop can be called multiple times and we only want to log once.
         if (hasCancelBeenLogged) {
             return
         }
@@ -66,42 +67,43 @@
         hasCancelBeenLogged = true
     }
 
-    public override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
-        window?.setGravity(Gravity.CENTER)
-        setContentView(R.layout.screen_share_dialog)
-        dialogTitle = requireViewById(R.id.screen_share_dialog_title)
-        warning = requireViewById(R.id.text_warning)
-        startButton = requireViewById(android.R.id.button1)
-        cancelButton = requireViewById(android.R.id.button2)
+    @CallSuper
+    override fun onCreate(dialog: T, savedInstanceState: Bundle?) {
+        this.dialog = dialog
+        dialog.window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+        dialog.window?.setGravity(Gravity.CENTER)
+        dialog.setContentView(R.layout.screen_share_dialog)
+        dialogTitle = dialog.requireViewById(R.id.screen_share_dialog_title)
+        warning = dialog.requireViewById(R.id.text_warning)
+        startButton = dialog.requireViewById(android.R.id.button1)
+        cancelButton = dialog.requireViewById(android.R.id.button2)
         updateIcon()
         initScreenShareOptions()
         createOptionsView(getOptionsViewLayoutId())
     }
 
     private fun updateIcon() {
-        val icon = requireViewById<ImageView>(R.id.screen_share_dialog_icon)
+        val icon = dialog.requireViewById<ImageView>(R.id.screen_share_dialog_icon)
         if (dialogIconTint != null) {
-            icon.setColorFilter(context.getColor(dialogIconTint))
+            icon.setColorFilter(dialog.context.getColor(dialogIconTint))
         }
         if (dialogIconDrawable != null) {
-            icon.setImageDrawable(context.getDrawable(dialogIconDrawable))
+            icon.setImageDrawable(dialog.context.getDrawable(dialogIconDrawable))
         }
     }
 
-    protected fun initScreenShareOptions() {
+    private fun initScreenShareOptions() {
         selectedScreenShareOption = screenShareOptions.first()
         warning.text = warningText
         initScreenShareSpinner()
     }
 
     private val warningText: String
-        get() = context.getString(selectedScreenShareOption.warningText, appName)
+        get() = dialog.context.getString(selectedScreenShareOption.warningText, appName)
 
     private fun initScreenShareSpinner() {
-        val adapter = OptionsAdapter(context.applicationContext, screenShareOptions)
-        screenShareModeSpinner = requireViewById(R.id.screen_share_mode_spinner)
+        val adapter = OptionsAdapter(dialog.context.applicationContext, screenShareOptions)
+        screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_spinner)
         screenShareModeSpinner.adapter = adapter
         screenShareModeSpinner.onItemSelectedListener = this
     }
@@ -115,7 +117,7 @@
 
     /** Protected methods for the text updates & functionality */
     protected fun setDialogTitle(@StringRes stringId: Int) {
-        val title = context.getString(stringId, appName)
+        val title = dialog.context.getString(stringId, appName)
         dialogTitle.text = title
     }
 
@@ -137,7 +139,7 @@
 
     private fun createOptionsView(@LayoutRes layoutId: Int?) {
         if (layoutId == null) return
-        val stub = requireViewById<View>(R.id.options_stub) as ViewStub
+        val stub = dialog.requireViewById<View>(R.id.options_stub) as ViewStub
         stub.layoutResource = layoutId
         stub.inflate()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index eacfa57..039372d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -60,6 +60,7 @@
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.phone.AlertDialogWithDelegate;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.util.Utils;
 
@@ -222,27 +223,30 @@
         // the correct screen width when in split screen.
         Context dialogContext = getApplicationContext();
         if (isPartialScreenSharingEnabled()) {
-            mDialog = new MediaProjectionPermissionDialog(
-                    dialogContext,
-                    getMediaProjectionConfig(),
-                    () -> {
-                        MediaProjectionPermissionDialog dialog =
-                                (MediaProjectionPermissionDialog) mDialog;
-                        ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
-                        grantMediaProjectionPermission(selectedOption.getMode());
-                    },
-                    () -> finish(RECORD_CANCEL, /* projection= */ null),
-                    appName,
-                    mUid,
-                    mMediaProjectionMetricsLogger);
+            MediaProjectionPermissionDialogDelegate delegate =
+                    new MediaProjectionPermissionDialogDelegate(
+                            dialogContext,
+                            getMediaProjectionConfig(),
+                            dialog -> {
+                                ScreenShareOption selectedOption =
+                                        dialog.getSelectedScreenShareOption();
+                                grantMediaProjectionPermission(selectedOption.getMode());
+                            },
+                            () -> finish(RECORD_CANCEL, /* projection= */ null),
+                            appName,
+                            mUid,
+                            mMediaProjectionMetricsLogger);
+            mDialog =
+                    new AlertDialogWithDelegate(
+                            dialogContext, R.style.Theme_SystemUI_Dialog, delegate);
         } else {
-            AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(dialogContext,
-                    R.style.Theme_SystemUI_Dialog)
-                    .setTitle(dialogTitle)
-                    .setIcon(R.drawable.ic_media_projection_permission)
-                    .setMessage(dialogText)
-                    .setPositiveButton(R.string.media_projection_action_text, this)
-                    .setNeutralButton(android.R.string.cancel, this);
+            AlertDialog.Builder dialogBuilder =
+                    new AlertDialog.Builder(dialogContext, R.style.Theme_SystemUI_Dialog)
+                            .setTitle(dialogTitle)
+                            .setIcon(R.drawable.ic_media_projection_permission)
+                            .setMessage(dialogText)
+                            .setPositiveButton(R.string.media_projection_action_text, this)
+                            .setNeutralButton(android.R.string.cancel, this);
             mDialog = dialogBuilder.create();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
index cff22b0..8453af1 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegate.kt
@@ -15,31 +15,32 @@
  */
 package com.android.systemui.mediaprojection.permission
 
+import android.app.AlertDialog
 import android.content.Context
 import android.media.projection.MediaProjectionConfig
 import android.os.Bundle
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
+import java.util.function.Consumer
 
 /** Dialog to select screen recording options */
-class MediaProjectionPermissionDialog(
+class MediaProjectionPermissionDialogDelegate(
     context: Context,
     mediaProjectionConfig: MediaProjectionConfig?,
-    private val onStartRecordingClicked: Runnable,
+    private val onStartRecordingClicked: Consumer<MediaProjectionPermissionDialogDelegate>,
     private val onCancelClicked: Runnable,
     private val appName: String?,
     hostUid: Int,
     mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
 ) :
-    BaseScreenSharePermissionDialog(
-        context,
+    BaseMediaProjectionPermissionDialogDelegate<AlertDialog>(
         createOptionList(context, appName, mediaProjectionConfig),
         appName,
         hostUid,
         mediaProjectionMetricsLogger
     ) {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
+    override fun onCreate(dialog: AlertDialog, savedInstanceState: Bundle?) {
+        super.onCreate(dialog, savedInstanceState)
         // TODO(b/270018943): Handle the case of System sharing (not recording nor casting)
         if (appName == null) {
             setDialogTitle(R.string.media_projection_entry_cast_permission_dialog_title)
@@ -51,12 +52,12 @@
         setStartButtonOnClickListener {
             // Note that it is important to run this callback before dismissing, so that the
             // callback can disable the dialog exit animation if it wants to.
-            onStartRecordingClicked.run()
-            dismiss()
+            onStartRecordingClicked.accept(this)
+            dialog.dismiss()
         }
         setCancelButtonOnClickListener {
             onCancelClicked.run()
-            dismiss()
+            dialog.dismiss()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 2928cce..79aedff 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -21,6 +21,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
+import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
 
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -49,8 +50,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.DisplayTracker;
@@ -83,7 +82,6 @@
     private final Context mContext;
     private final Handler mHandler;
     private final NavigationBarComponent.Factory mNavigationBarComponentFactory;
-    private FeatureFlags mFeatureFlags;
     private final SecureSettings mSecureSettings;
     private final DisplayTracker mDisplayTracker;
     private final DisplayManager mDisplayManager;
@@ -118,13 +116,11 @@
             TaskStackChangeListeners taskStackChangeListeners,
             Optional<Pip> pipOptional,
             Optional<BackAnimation> backAnimation,
-            FeatureFlags featureFlags,
             SecureSettings secureSettings,
             DisplayTracker displayTracker) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarComponentFactory = navigationBarComponentFactory;
-        mFeatureFlags = featureFlags;
         mSecureSettings = secureSettings;
         mDisplayTracker = displayTracker;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
@@ -248,8 +244,8 @@
     /** @return {@code true} if taskbar is enabled, false otherwise */
     private boolean initializeTaskbarIfNecessary() {
         // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen
-        boolean taskbarEnabled = (mIsLargeScreen || mFeatureFlags.isEnabled(
-                Flags.HIDE_NAVBAR_WINDOW)) && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
+        boolean taskbarEnabled = (mIsLargeScreen || enableTaskbarNavbarUnification())
+                && shouldCreateNavBarAndTaskBar(mContext.getDisplayId());
 
         if (taskbarEnabled) {
             Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index c77f3f4..fa03dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -22,7 +22,6 @@
 import android.graphics.Point
 import android.os.Handler
 import android.os.SystemClock
-import android.os.VibrationEffect
 import android.util.Log
 import android.util.MathUtils
 import android.view.Gravity
@@ -37,8 +36,6 @@
 import androidx.dynamicanimation.animation.DynamicAnimation
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.plugins.NavigationEdgeBackPlugin
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -78,12 +75,6 @@
 private const val POP_ON_INACTIVE_TO_ACTIVE_VELOCITY = 4.7f
 private const val POP_ON_INACTIVE_VELOCITY = -1.5f
 
-internal val VIBRATE_ACTIVATED_EFFECT =
-    VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
-
-internal val VIBRATE_DEACTIVATED_EFFECT =
-    VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
-
 private const val DEBUG = false
 
 class BackPanelController
@@ -95,7 +86,6 @@
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
     private val latencyTracker: LatencyTracker,
-    private val featureFlags: FeatureFlags
 ) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
 
     /**
@@ -113,7 +103,6 @@
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
         private val latencyTracker: LatencyTracker,
-        private val featureFlags: FeatureFlags
     ) {
         /** Construct a [BackPanelController]. */
         fun create(context: Context): BackPanelController {
@@ -126,7 +115,6 @@
                     vibratorHelper,
                     configurationController,
                     latencyTracker,
-                    featureFlags
                 )
             backPanelController.init()
             return backPanelController
@@ -992,35 +980,22 @@
                 val springForceOnCancelled =
                     params.cancelledIndicator.arrowDimens.alphaSpring?.get(0f)?.value
                 mView.popArrowAlpha(0f, springForceOnCancelled)
-                if (!featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION))
-                    mainHandler.postDelayed(10L) { vibratorHelper.cancel() }
             }
         }
     }
 
     private fun performDeactivatedHapticFeedback() {
-        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            vibratorHelper.performHapticFeedback(
-                    mView,
-                    HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
-            )
-        } else {
-            vibratorHelper.vibrate(VIBRATE_DEACTIVATED_EFFECT)
-        }
+        vibratorHelper.performHapticFeedback(
+                mView,
+                HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+        )
     }
 
     private fun performActivatedHapticFeedback() {
-        if (featureFlags.isEnabled(ONE_WAY_HAPTICS_API_MIGRATION)) {
-            vibratorHelper.performHapticFeedback(
-                    mView,
-                    HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
-            )
-        } else {
-            vibratorHelper.cancel()
-            mainHandler.postDelayed(10L) {
-                vibratorHelper.vibrate(VIBRATE_ACTIVATED_EFFECT)
-            }
-        }
+        vibratorHelper.performHapticFeedback(
+                mView,
+                HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+        )
     }
 
     private fun convertVelocityToAnimationFactor(
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 05f125f..bff0b93 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -46,6 +46,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.CallbackController;
 
 import dagger.Lazy;
@@ -75,6 +76,7 @@
     private final UserContextProvider mUserContextProvider;
     private final UserTracker mUserTracker;
     private final MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
+    private final SystemUIDialog.Factory mDialogFactory;
 
     protected static final String INTENT_UPDATE_STATE =
             "com.android.systemui.screenrecord.UPDATE_STATE";
@@ -120,7 +122,8 @@
             UserContextProvider userContextProvider,
             Lazy<ScreenCaptureDevicePolicyResolver> devicePolicyResolver,
             UserTracker userTracker,
-            MediaProjectionMetricsLogger mediaProjectionMetricsLogger) {
+            MediaProjectionMetricsLogger mediaProjectionMetricsLogger,
+            SystemUIDialog.Factory dialogFactory) {
         mMainExecutor = mainExecutor;
         mContext = context;
         mFlags = flags;
@@ -129,6 +132,7 @@
         mUserContextProvider = userContextProvider;
         mUserTracker = userTracker;
         mMediaProjectionMetricsLogger = mediaProjectionMetricsLogger;
+        mDialogFactory = dialogFactory;
 
         BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setInteractive(true);
@@ -166,15 +170,14 @@
                 getHostUid(), SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
 
         return flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)
-                ? new ScreenRecordPermissionDialog(
-                        context,
-                        getHostUserHandle(),
-                        getHostUid(),
-                        /* controller= */ this,
-                        activityStarter,
-                        mUserContextProvider,
-                        onStartRecordingClicked,
-                        mMediaProjectionMetricsLogger)
+                ? mDialogFactory.create(new ScreenRecordPermissionDialogDelegate(
+                getHostUserHandle(),
+                getHostUid(),
+                /* controller= */ this,
+                activityStarter,
+                mUserContextProvider,
+                onStartRecordingClicked,
+                mMediaProjectionMetricsLogger))
                 : new ScreenRecordDialog(
                         context,
                         /* controller= */ this,
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
rename to packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index f74234b..e57a0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -17,7 +17,6 @@
 
 import android.app.Activity
 import android.app.PendingIntent
-import android.content.Context
 import android.content.Intent
 import android.os.Bundle
 import android.os.Handler
@@ -35,17 +34,17 @@
 import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
-import com.android.systemui.mediaprojection.permission.BaseScreenSharePermissionDialog
+import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionDialogDelegate
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
 import com.android.systemui.mediaprojection.permission.ScreenShareOption
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.SystemUIDialog
 
 /** Dialog to select screen recording options */
-class ScreenRecordPermissionDialog(
-    context: Context,
+class ScreenRecordPermissionDialogDelegate(
     private val hostUserHandle: UserHandle,
     private val hostUid: Int,
     private val controller: RecordingController,
@@ -54,8 +53,7 @@
     private val onStartRecordingClicked: Runnable?,
     mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
 ) :
-    BaseScreenSharePermissionDialog(
-        context,
+    BaseMediaProjectionPermissionDialogDelegate<SystemUIDialog>(
         createOptionList(),
         appName = null,
         hostUid = hostUid,
@@ -68,10 +66,10 @@
     private lateinit var audioSwitch: Switch
     private lateinit var options: Spinner
 
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
+    override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        super.onCreate(dialog, savedInstanceState)
         setDialogTitle(R.string.screenrecord_permission_dialog_title)
-        setTitle(R.string.screenrecord_title)
+        dialog.setTitle(R.string.screenrecord_title)
         setStartButtonText(R.string.screenrecord_permission_dialog_continue)
         setStartButtonOnClickListener { v: View? ->
             onStartRecordingClicked?.run()
@@ -79,7 +77,7 @@
                 requestScreenCapture(/* captureTarget= */ null)
             }
             if (selectedScreenShareOption.mode == SINGLE_APP) {
-                val intent = Intent(context, MediaProjectionAppSelectorActivity::class.java)
+                val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
 
                 // We can't start activity for result here so we use result receiver to get
@@ -96,22 +94,26 @@
                 intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
                 activityStarter.startActivity(intent, /* dismissShade= */ true)
             }
-            dismiss()
+            dialog.dismiss()
         }
-        setCancelButtonOnClickListener { dismiss() }
+        setCancelButtonOnClickListener { dialog.dismiss() }
         initRecordOptionsView()
     }
 
     @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
 
     private fun initRecordOptionsView() {
-        audioSwitch = requireViewById(R.id.screenrecord_audio_switch)
-        tapsSwitch = requireViewById(R.id.screenrecord_taps_switch)
-        tapsView = requireViewById(R.id.show_taps)
+        audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
+        tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
+        tapsView = dialog.requireViewById(R.id.show_taps)
         updateTapsViewVisibility()
-        options = requireViewById(R.id.screen_recording_options)
+        options = dialog.requireViewById(R.id.screen_recording_options)
         val a: ArrayAdapter<*> =
-            ScreenRecordingAdapter(context, android.R.layout.simple_spinner_dropdown_item, MODES)
+            ScreenRecordingAdapter(
+                dialog.context,
+                android.R.layout.simple_spinner_dropdown_item,
+                MODES
+            )
         a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
         options.adapter = a
         options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
index 03be88f..c59ef26 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
@@ -18,13 +18,15 @@
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.smartspace.SmartspacePrecondition
 import com.android.systemui.smartspace.SmartspaceTargetFilter
+import com.android.systemui.smartspace.data.repository.SmartspaceRepositoryModule
 import com.android.systemui.smartspace.preconditions.LockscreenPrecondition
 import dagger.Binds
 import dagger.BindsOptionalOf
 import dagger.Module
 import javax.inject.Named
 
-@Module(subcomponents = [SmartspaceViewComponent::class])
+@Module(subcomponents = [SmartspaceViewComponent::class],
+    includes = [SmartspaceRepositoryModule::class])
 abstract class SmartspaceModule {
     @Module
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
new file mode 100644
index 0000000..2fc0ec2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepository.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.smartspace.data.repository
+
+import android.app.smartspace.SmartspaceTarget
+import android.os.Parcelable
+import android.widget.RemoteViews
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.BcSmartspaceDataPlugin
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onStart
+
+interface SmartspaceRepository {
+    /** Whether [RemoteViews] are passed through smartspace targets. */
+    val isSmartspaceRemoteViewsEnabled: Boolean
+
+    /** Smartspace targets for the lockscreen surface. */
+    val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>>
+}
+
+@SysUISingleton
+class SmartspaceRepositoryImpl
+@Inject
+constructor(
+    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+) : SmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {
+
+    override val isSmartspaceRemoteViewsEnabled: Boolean
+        get() = android.app.smartspace.flags.Flags.remoteViews()
+
+    private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+        MutableStateFlow(emptyList())
+    override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+        _lockscreenSmartspaceTargets
+            .onStart {
+                lockscreenSmartspaceController.addListener(listener = this@SmartspaceRepositoryImpl)
+            }
+            .onCompletion {
+                lockscreenSmartspaceController.removeListener(
+                    listener = this@SmartspaceRepositoryImpl
+                )
+            }
+
+    override fun onSmartspaceTargetsUpdated(targetsNullable: MutableList<out Parcelable>?) {
+        targetsNullable?.let { targets ->
+            _lockscreenSmartspaceTargets.value = targets.filterIsInstance<SmartspaceTarget>()
+        }
+            ?: run { _lockscreenSmartspaceTargets.value = emptyList() }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt
new file mode 100644
index 0000000..c77bcc5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/data/repository/SmartspaceRepositoryModule.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.smartspace.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface SmartspaceRepositoryModule {
+    @Binds fun smartspaceRepository(impl: SmartspaceRepositoryImpl): SmartspaceRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index 65b798a..62c3e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -18,7 +18,6 @@
 import android.graphics.Color
 import android.graphics.Rect
 import android.view.View
-import android.view.ViewGroup
 import android.widget.FrameLayout
 import androidx.annotation.ColorInt
 import androidx.collection.ArrayMap
@@ -32,7 +31,6 @@
 import com.android.systemui.statusbar.notification.NotificationUtils
 import com.android.systemui.statusbar.notification.collection.NotifCollection
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
-import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColorLookup
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconColors
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerShelfViewModel
@@ -52,6 +50,7 @@
 import com.android.systemui.util.ui.value
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -88,14 +87,17 @@
         return view.repeatWhenAttached {
             lifecycleScope.run {
                 launch {
+                    val iconColors =
+                        viewModel.iconColors.mapNotNull { it.iconColors(view.viewBounds) }
                     viewModel.icons.bindIcons(
                         view,
                         configuration,
                         configurationController,
-                        viewStore
-                    )
+                        viewStore,
+                    ) { _, sbiv ->
+                        iconColors.collect { sbiv.updateTintForIcon(it, contrastColorUtil) }
+                    }
                 }
-                launch { viewModel.iconColors.bindIconColors(view, contrastColorUtil) }
                 launch { viewModel.bindIsolatedIcon(view, viewStore) }
                 launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
             }
@@ -119,15 +121,17 @@
                         configuration,
                         configurationController,
                         viewStore,
-                    )
+                    ) { _, sbiv ->
+                        configuration
+                            .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
+                            .collect { tint ->
+                                sbiv.staticDrawableColor = tint
+                                sbiv.setDecorColor(tint)
+                            }
+                    }
                 }
                 launch { viewModel.animationsEnabled.bindAnimationsEnabled(view) }
                 launch { viewModel.isDozing.bindIsDozing(view, dozeParameters) }
-                launch {
-                    configuration
-                        .getColorAttr(R.attr.wallpaperTextColor, DEFAULT_AOD_ICON_COLOR)
-                        .bindIconColors(view)
-                }
             }
         }
     }
@@ -137,31 +141,6 @@
         collect(view::setAnimationsEnabled)
     }
 
-    /**
-     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
-     * of the [children] of an [NotificationIconContainer].
-     */
-    private suspend fun Flow<NotificationIconColorLookup>.bindIconColors(
-        view: NotificationIconContainer,
-        contrastColorUtil: ContrastColorUtil,
-    ) {
-        mapNotNull { lookup -> lookup.iconColors(view.viewBounds) }
-            .collect { iconLookup -> view.applyTint(iconLookup, contrastColorUtil) }
-    }
-
-    /**
-     * Binds to the [StatusBarIconView.setStaticDrawableColor] and [StatusBarIconView.setDecorColor]
-     * of the [children] of an [NotificationIconContainer].
-     */
-    private suspend fun Flow<Int>.bindIconColors(view: NotificationIconContainer) {
-        collect { tint ->
-            view.children.filterIsInstance<StatusBarIconView>().forEach { icon ->
-                icon.staticDrawableColor = tint
-                icon.setDecorColor(tint)
-            }
-        }
-    }
-
     private suspend fun Flow<AnimatedValue<Boolean>>.bindIsDozing(
         view: NotificationIconContainer,
         dozeParameters: DozeParameters,
@@ -208,12 +187,19 @@
         }
     }
 
-    /** Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children]. */
+    /**
+     * Binds [NotificationIconsViewData] to a [NotificationIconContainer]'s [children].
+     *
+     * [bindIcon] will be invoked to bind a child [StatusBarIconView] to an icon associated with the
+     * given `iconKey`. The parent [Job] of this coroutine will be cancelled automatically when the
+     * view is to be unbound.
+     */
     private suspend fun Flow<NotificationIconsViewData>.bindIcons(
         view: NotificationIconContainer,
         configuration: ConfigurationState,
         configurationController: ConfigurationController,
         viewStore: IconViewStore,
+        bindIcon: suspend (iconKey: String, view: StatusBarIconView) -> Unit = { _, _ -> },
     ): Unit = coroutineScope {
         val iconSizeFlow: Flow<Int> =
             configuration.getDimensionPixelSize(
@@ -242,6 +228,7 @@
             }
         }
 
+        val iconBindings = mutableMapOf<String, Job>()
         var prevIcons = NotificationIconsViewData()
         sample(layoutParams, ::Pair).collect {
             (iconsData: NotificationIconsViewData, layoutParams: FrameLayout.LayoutParams),
@@ -261,15 +248,20 @@
                 }
 
             iconsDiff.removed
-                .mapNotNull { key -> childrenByNotifKey[key] }
-                .forEach { child -> view.removeView(child) }
+                .mapNotNull { key -> childrenByNotifKey[key]?.let { key to it } }
+                .forEach { (key, child) ->
+                    view.removeView(child)
+                    iconBindings.remove(key)?.cancel()
+                }
 
-            val toAdd = iconsDiff.added.map { viewStore.iconView(it.notifKey) }
-            for ((i, sbiv) in toAdd.withIndex()) {
+            val toAdd = iconsDiff.added.map { it.notifKey to viewStore.iconView(it.notifKey) }
+            for ((i, keyAndView) in toAdd.withIndex()) {
+                val (key, sbiv) = keyAndView
                 // The view might still be transiently added if it was just removed
                 // and added again
                 view.removeTransientView(sbiv)
                 view.addView(sbiv, i, layoutParams)
+                iconBindings[key] = launch { bindIcon(key, sbiv) }
             }
 
             view.setChangingViewPositions(true)
@@ -292,16 +284,6 @@
 
     // TODO(b/305739416): Once StatusBarIconView has its own Recommended Architecture stack, this
     //  can be moved there and cleaned up.
-    private fun ViewGroup.applyTint(
-        iconColors: NotificationIconColors,
-        contrastColorUtil: ContrastColorUtil,
-    ) {
-        children
-            .filterIsInstance<StatusBarIconView>()
-            .filter { it.width != 0 }
-            .forEach { iv -> iv.updateTintForIcon(iconColors, contrastColorUtil) }
-    }
-
     private fun StatusBarIconView.updateTintForIcon(
         iconColors: NotificationIconColors,
         contrastColorUtil: ContrastColorUtil,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 2f17b6f..5460a1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -17,14 +17,25 @@
 
 package com.android.systemui.communal.domain.interactor
 
+import android.app.smartspace.SmartspaceTarget
+import android.provider.Settings
+import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
+import android.widget.RemoteViews
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalAppWidgetInfo
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.TestScope
@@ -34,6 +45,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -44,24 +56,35 @@
 
     private lateinit var testScope: TestScope
 
+    private lateinit var tutorialRepository: FakeCommunalTutorialRepository
     private lateinit var communalRepository: FakeCommunalRepository
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
-    private lateinit var interactor: CommunalInteractor
+    private lateinit var smartspaceRepository: FakeSmartspaceRepository
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+
+    private lateinit var underTest: CommunalInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
         testScope = TestScope()
-        communalRepository = FakeCommunalRepository()
-        widgetRepository = FakeCommunalWidgetRepository()
-        interactor = CommunalInteractor(communalRepository, widgetRepository)
+
+        val withDeps = CommunalInteractorFactory.create()
+
+        tutorialRepository = withDeps.tutorialRepository
+        communalRepository = withDeps.communalRepository
+        widgetRepository = withDeps.widgetRepository
+        smartspaceRepository = withDeps.smartspaceRepository
+        keyguardRepository = withDeps.keyguardRepository
+
+        underTest = withDeps.communalInteractor
     }
 
     @Test
     fun appWidgetInfoFlow() =
         testScope.runTest {
-            val lastAppWidgetInfo = collectLastValue(interactor.appWidgetInfo)
+            val lastAppWidgetInfo = collectLastValue(underTest.appWidgetInfo)
             runCurrent()
             assertThat(lastAppWidgetInfo()).isNull()
 
@@ -74,31 +97,155 @@
     fun communalEnabled() =
         testScope.runTest {
             communalRepository.setIsCommunalEnabled(true)
-
-            val interactor = CommunalInteractor(communalRepository, widgetRepository)
-            assertThat(interactor.isCommunalEnabled).isTrue()
+            assertThat(underTest.isCommunalEnabled).isTrue()
         }
 
     @Test
     fun communalDisabled() =
         testScope.runTest {
             communalRepository.setIsCommunalEnabled(false)
+            assertThat(underTest.isCommunalEnabled).isFalse()
+        }
 
-            val interactor = CommunalInteractor(communalRepository, widgetRepository)
-            assertThat(interactor.isCommunalEnabled).isFalse()
+    @Test
+    fun tutorial_tutorialNotCompletedAndKeyguardVisible_showTutorialContent() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial not started.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(
+                Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
+            )
+
+            val communalContent by collectLastValue(underTest.communalContent)
+
+            assertThat(communalContent!!).isNotEmpty()
+            communalContent!!.forEach { model ->
+                assertThat(model is CommunalContentModel.Tutorial).isTrue()
+            }
+        }
+
+    @Test
+    fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            // Widgets are available.
+            val widgets =
+                listOf(
+                    CommunalWidgetContentModel(
+                        appWidgetId = 0,
+                        priority = 30,
+                        providerInfo = mock(),
+                    ),
+                    CommunalWidgetContentModel(
+                        appWidgetId = 1,
+                        priority = 20,
+                        providerInfo = mock(),
+                    ),
+                    CommunalWidgetContentModel(
+                        appWidgetId = 2,
+                        priority = 10,
+                        providerInfo = mock(),
+                    ),
+                )
+            widgetRepository.setCommunalWidgets(widgets)
+
+            val communalContent by collectLastValue(underTest.communalContent)
+
+            assertThat(communalContent!!).isNotEmpty()
+            communalContent!!.forEachIndexed { index, model ->
+                assertThat((model as CommunalContentModel.Widget).appWidgetId)
+                    .isEqualTo(widgets[index].appWidgetId)
+            }
+        }
+
+    @Test
+    fun smartspace_onlyShowTimersWithRemoteViews() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            // Not a timer
+            val target1 = mock(SmartspaceTarget::class.java)
+            whenever(target1.smartspaceTargetId).thenReturn("target1")
+            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_WEATHER)
+            whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+
+            // Does not have RemoteViews
+            val target2 = mock(SmartspaceTarget::class.java)
+            whenever(target1.smartspaceTargetId).thenReturn("target2")
+            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target1.remoteViews).thenReturn(null)
+
+            // Timer and has RemoteViews
+            val target3 = mock(SmartspaceTarget::class.java)
+            whenever(target1.smartspaceTargetId).thenReturn("target3")
+            whenever(target1.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target1.remoteViews).thenReturn(mock(RemoteViews::class.java))
+
+            val targets = listOf(target1, target2, target3)
+            smartspaceRepository.setLockscreenSmartspaceTargets(targets)
+
+            val communalContent by collectLastValue(underTest.communalContent)
+            assertThat(communalContent?.size).isEqualTo(1)
+            assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target3")
+        }
+
+    @Test
+    fun smartspace_smartspaceAndWidgetsAvailable_showSmartspaceAndWidgetContent() =
+        testScope.runTest {
+            // Keyguard showing, and tutorial completed.
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+
+            // Widgets available.
+            val widgets =
+                listOf(
+                    CommunalWidgetContentModel(
+                        appWidgetId = 0,
+                        priority = 30,
+                        providerInfo = mock(),
+                    ),
+                    CommunalWidgetContentModel(
+                        appWidgetId = 1,
+                        priority = 20,
+                        providerInfo = mock(),
+                    ),
+                )
+            widgetRepository.setCommunalWidgets(widgets)
+
+            // Smartspace available.
+            val target = mock(SmartspaceTarget::class.java)
+            whenever(target.smartspaceTargetId).thenReturn("target")
+            whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target.remoteViews).thenReturn(mock(RemoteViews::class.java))
+            smartspaceRepository.setLockscreenSmartspaceTargets(listOf(target))
+
+            val communalContent by collectLastValue(underTest.communalContent)
+
+            assertThat(communalContent?.size).isEqualTo(3)
+            assertThat(communalContent?.get(0)?.key).isEqualTo("smartspace_target")
+            assertThat(communalContent?.get(1)?.key).isEqualTo("widget_0")
+            assertThat(communalContent?.get(2)?.key).isEqualTo("widget_1")
         }
 
     @Test
     fun listensToSceneChange() =
         testScope.runTest {
-            val interactor = CommunalInteractor(communalRepository, widgetRepository)
-            var desiredScene = collectLastValue(interactor.desiredScene)
+            var desiredScene = collectLastValue(underTest.desiredScene)
             runCurrent()
             assertThat(desiredScene()).isEqualTo(CommunalSceneKey.Blank)
 
             val targetScene = CommunalSceneKey.Communal
             communalRepository.setDesiredScene(targetScene)
-            desiredScene = collectLastValue(interactor.desiredScene)
+            desiredScene = collectLastValue(underTest.desiredScene)
             runCurrent()
             assertThat(desiredScene()).isEqualTo(targetScene)
         }
@@ -106,10 +253,9 @@
     @Test
     fun updatesScene() =
         testScope.runTest {
-            val interactor = CommunalInteractor(communalRepository, widgetRepository)
             val targetScene = CommunalSceneKey.Communal
 
-            interactor.onSceneChanged(targetScene)
+            underTest.onSceneChanged(targetScene)
 
             val desiredScene = collectLastValue(communalRepository.desiredScene)
             runCurrent()
@@ -119,15 +265,13 @@
     @Test
     fun isCommunalShowing() =
         testScope.runTest {
-            val interactor = CommunalInteractor(communalRepository, widgetRepository)
-
-            var isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+            var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
             runCurrent()
             assertThat(isCommunalShowing()).isEqualTo(false)
 
-            interactor.onSceneChanged(CommunalSceneKey.Communal)
+            underTest.onSceneChanged(CommunalSceneKey.Communal)
 
-            isCommunalShowing = collectLastValue(interactor.isCommunalShowing)
+            isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
             runCurrent()
             assertThat(isCommunalShowing()).isEqualTo(true)
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
deleted file mode 100644
index 61d1502..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.domain.interactor
-
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED
-import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_STARTED
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
-import com.android.systemui.communal.shared.model.CommunalSceneKey
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(JUnit4::class)
-class CommunalTutorialInteractorTest : SysuiTestCase() {
-
-    @Mock private lateinit var userTracker: UserTracker
-
-    private lateinit var testScope: TestScope
-    private lateinit var underTest: CommunalTutorialInteractor
-    private lateinit var keyguardRepository: FakeKeyguardRepository
-    private lateinit var keyguardInteractor: KeyguardInteractor
-    private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
-    private lateinit var sceneContainerFlags: FakeSceneContainerFlags
-    private lateinit var communalInteractor: CommunalInteractor
-    private lateinit var communalRepository: FakeCommunalRepository
-
-    private val utils = SceneTestUtils(this)
-    private lateinit var sceneInteractor: SceneInteractor
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        sceneInteractor = utils.sceneInteractor()
-        testScope = utils.testScope
-        sceneContainerFlags = utils.sceneContainerFlags.apply { enabled = false }
-        communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
-        communalInteractor = CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
-
-        val withDeps = KeyguardInteractorFactory.create()
-        keyguardInteractor = withDeps.keyguardInteractor
-        keyguardRepository = withDeps.repository
-        communalTutorialRepository = FakeCommunalTutorialRepository()
-
-        underTest =
-            CommunalTutorialInteractor(
-                scope = testScope.backgroundScope,
-                communalTutorialRepository = communalTutorialRepository,
-                keyguardInteractor = keyguardInteractor,
-                communalInteractor = communalInteractor,
-                sceneContainerFlags = sceneContainerFlags,
-                sceneInteractor = sceneInteractor,
-            )
-
-        whenever(userTracker.userHandle).thenReturn(mock())
-    }
-
-    @Test
-    fun tutorialUnavailable_whenKeyguardNotVisible() =
-        testScope.runTest {
-            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-            keyguardRepository.setKeyguardShowing(false)
-            assertThat(isTutorialAvailable).isFalse()
-        }
-
-    @Test
-    fun tutorialUnavailable_whenTutorialIsCompleted() =
-        testScope.runTest {
-            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-            assertThat(isTutorialAvailable).isFalse()
-        }
-
-    @Test
-    fun tutorialAvailable_whenTutorialNotStarted() =
-        testScope.runTest {
-            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-            assertThat(isTutorialAvailable).isTrue()
-        }
-
-    @Test
-    fun tutorialAvailable_whenTutorialIsStarted() =
-        testScope.runTest {
-            val isTutorialAvailable by collectLastValue(underTest.isTutorialAvailable)
-            keyguardRepository.setKeyguardShowing(true)
-            keyguardRepository.setKeyguardOccluded(false)
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-            assertThat(isTutorialAvailable).isTrue()
-        }
-
-    /* Testing tutorial states with transitions when flexiglass off */
-    @Test
-    fun tutorialState_notStartedAndCommunalSceneShowing_tutorialStarted() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
-        }
-
-    @Test
-    fun tutorialState_startedAndCommunalSceneShowing_stateWillNotUpdate() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
-        }
-
-    @Test
-    fun tutorialState_completedAndCommunalSceneShowing_stateWillNotUpdate() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Communal)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-
-    @Test
-    fun tutorialState_notStartedAndCommunalSceneNotShowing_stateWillNotUpdate() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
-        }
-
-    @Test
-    fun tutorialState_startedAndCommunalSceneNotShowing_tutorialCompleted() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-
-    @Test
-    fun tutorialState_completedAndCommunalSceneNotShowing_stateWillNotUpdate() =
-        testScope.runTest {
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(communalInteractor.desiredScene)
-            communalInteractor.onSceneChanged(CommunalSceneKey.Communal)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
-            communalInteractor.onSceneChanged(CommunalSceneKey.Blank)
-
-            assertThat(currentScene).isEqualTo(CommunalSceneKey.Blank)
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-
-    /* Testing tutorial states with transitions when flexiglass on */
-    @Test
-    fun tutorialState_notStartedCommunalSceneShowingAndFlexiglassOn_tutorialStarted() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
-        }
-
-    @Test
-    fun tutorialState_startedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_STARTED)
-        }
-
-    @Test
-    fun tutorialState_completedCommunalSceneShowingAndFlexiglassOn_stateWillNotUpdate() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Communal))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-
-    @Test
-    fun tutorialState_notStartedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
-        }
-
-    @Test
-    fun tutorialState_startedCommunalSceneNotShowingAndFlexiglassOn_tutorialCompleted() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-
-    @Test
-    fun tutorialState_completedCommunalSceneNotShowingAndFlexiglassOn_stateWillNotUpdate() =
-        testScope.runTest {
-            sceneContainerFlags.enabled = true
-            val tutorialSettingState by
-                collectLastValue(communalTutorialRepository.tutorialSettingState)
-            val currentScene by collectLastValue(sceneInteractor.desiredScene)
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Communal), "reason")
-            communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
-            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen), "reason")
-
-            assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
-            assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 0db3de2..1f18705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -63,7 +63,8 @@
         ConnectedDisplayInteractorImpl(
             virtualDeviceManager,
             fakeKeyguardRepository,
-            fakeDisplayRepository
+            fakeDisplayRepository,
+            UnconfinedTestDispatcher(),
         )
     private val testScope = TestScope(UnconfinedTestDispatcher())
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
index 5bfe569..a2eb5ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaHierarchyManagerTest.kt
@@ -26,8 +26,7 @@
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
 import com.android.systemui.communal.shared.model.CommunalSceneKey
 import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.dreams.DreamOverlayStateController
@@ -106,7 +105,7 @@
     private val configurationController = FakeConfigurationController()
     private val communalRepository = FakeCommunalRepository(isCommunalEnabled = true)
     private val communalInteractor =
-        CommunalInteractor(communalRepository, FakeCommunalWidgetRepository())
+        CommunalInteractorFactory.create(communalRepository = communalRepository).communalInteractor
     private val notifPanelEvents = ShadeExpansionStateManager()
     private val settings = FakeSettings()
     private lateinit var testableLooper: TestableLooper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index c835146..8a531fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -111,7 +111,6 @@
                         TaskStackChangeListeners.getTestInstance(),
                         Optional.of(mock(Pip.class)),
                         Optional.of(mock(BackAnimation.class)),
-                        mock(FeatureFlags.class),
                         mock(SecureSettings.class),
                         mDisplayTracker));
         initializeNavigationBars();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index 2d3dc58..f93d52b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -30,8 +30,6 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION
 import com.android.systemui.plugins.NavigationEdgeBackPlugin
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -63,7 +61,6 @@
     @Mock private lateinit var latencyTracker: LatencyTracker
     @Mock private lateinit var layoutParams: WindowManager.LayoutParams
     @Mock private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback
-    private val featureFlags = FakeFeatureFlags()
 
     @Before
     fun setup() {
@@ -77,7 +74,6 @@
                 vibratorHelper,
                 configurationController,
                 latencyTracker,
-                featureFlags
             )
         mBackPanelController.setLayoutParams(layoutParams)
         mBackPanelController.setBackCallback(backCallback)
@@ -106,32 +102,6 @@
 
     @Test
     fun handlesBackCommitted() {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
-        startTouch()
-        // Move once to cross the touch slop
-        continueTouch(START_X + touchSlop.toFloat() + 1)
-        // Move again to cross the back trigger threshold
-        continueTouch(START_X + touchSlop + triggerThreshold + 1)
-        // Wait threshold duration and hold touch past trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
-        continueTouch(START_X + touchSlop + triggerThreshold + 1)
-
-        assertThat(mBackPanelController.currentState)
-            .isEqualTo(BackPanelController.GestureState.ACTIVE)
-        verify(backCallback).setTriggerBack(true)
-        testableLooper.moveTimeForward(100)
-        testableLooper.processAllMessages()
-        verify(vibratorHelper).vibrate(VIBRATE_ACTIVATED_EFFECT)
-
-        finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
-        assertThat(mBackPanelController.currentState)
-            .isEqualTo(BackPanelController.GestureState.COMMITTED)
-        verify(backCallback).triggerBack()
-    }
-
-    @Test
-    fun handlesBackCommitted_withOneWayHapticsAPI() {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
@@ -148,7 +118,6 @@
         testableLooper.processAllMessages()
         verify(vibratorHelper)
             .performHapticFeedback(any(), eq(HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE))
-
         finishTouchActionUp(START_X + touchSlop + triggerThreshold + 1)
         assertThat(mBackPanelController.currentState)
             .isEqualTo(BackPanelController.GestureState.COMMITTED)
@@ -157,38 +126,6 @@
 
     @Test
     fun handlesBackCancelled() {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false)
-        startTouch()
-        // Move once to cross the touch slop
-        continueTouch(START_X + touchSlop.toFloat() + 1)
-        // Move again to cross the back trigger threshold
-        continueTouch(
-            START_X + touchSlop + triggerThreshold -
-                mBackPanelController.params.deactivationTriggerThreshold
-        )
-        // Wait threshold duration and hold touch before trigger threshold
-        Thread.sleep((MAX_DURATION_ENTRY_BEFORE_ACTIVE_ANIMATION + 1).toLong())
-        continueTouch(
-            START_X + touchSlop + triggerThreshold -
-                mBackPanelController.params.deactivationTriggerThreshold
-        )
-        clearInvocations(backCallback)
-        Thread.sleep(MIN_DURATION_ACTIVE_BEFORE_INACTIVE_ANIMATION)
-        // Move in the opposite direction to cross the deactivation threshold and cancel back
-        continueTouch(START_X)
-
-        assertThat(mBackPanelController.currentState)
-            .isEqualTo(BackPanelController.GestureState.INACTIVE)
-        verify(backCallback).setTriggerBack(false)
-        verify(vibratorHelper).vibrate(VIBRATE_DEACTIVATED_EFFECT)
-
-        finishTouchActionUp(START_X)
-        verify(backCallback).cancelBack()
-    }
-
-    @Test
-    fun handlesBackCancelled_withOneWayHapticsAPI() {
-        featureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true)
         startTouch()
         // Move once to cross the touch slop
         continueTouch(START_X + touchSlop.toFloat() + 1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
index 49049bc..1b4ba64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java
@@ -36,20 +36,27 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger;
 import com.android.systemui.mediaprojection.SessionCreationSource;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver;
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialog;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.DialogDelegate;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -92,6 +99,7 @@
 
     private FakeFeatureFlags mFeatureFlags;
     private RecordingController mController;
+    private TestSystemUIDialogFactory mDialogFactory;
 
     private static final int USER_ID = 10;
 
@@ -103,6 +111,15 @@
 
         when(mUserContextProvider.getUserContext()).thenReturn(spiedContext);
 
+        mDialogFactory = new TestSystemUIDialogFactory(
+                mContext,
+                mFeatureFlags,
+                Dependency.get(SystemUIDialogManager.class),
+                Dependency.get(SysUiState.class),
+                Dependency.get(BroadcastDispatcher.class),
+                Dependency.get(DialogLaunchAnimator.class)
+        );
+
         mFeatureFlags = new FakeFeatureFlags();
         mController = new RecordingController(
                 mMainExecutor,
@@ -112,7 +129,8 @@
                 mUserContextProvider,
                 () -> mDevicePolicyResolver,
                 mUserTracker,
-                mMediaProjectionMetricsLogger);
+                mMediaProjectionMetricsLogger,
+                mDialogFactory);
         mController.addCallback(mCallback);
     }
 
@@ -218,10 +236,17 @@
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, false);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(true);
 
-        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
-                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+        Dialog dialog =
+                mController.createScreenRecordDialog(
+                        mContext,
+                        mFeatureFlags,
+                        mDialogLaunchAnimator,
+                        mActivityStarter,
+                        /* onStartRecordingClicked= */ null);
 
-        assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+        assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
+        assertThat(mDialogFactory.mLastDelegate)
+                .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
     }
 
     @Test
@@ -253,10 +278,17 @@
         mFeatureFlags.set(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES, true);
         when(mDevicePolicyResolver.isScreenCaptureCompletelyDisabled((any()))).thenReturn(false);
 
-        Dialog dialog = mController.createScreenRecordDialog(mContext, mFeatureFlags,
-                mDialogLaunchAnimator, mActivityStarter, /* onStartRecordingClicked= */ null);
+        Dialog dialog =
+                mController.createScreenRecordDialog(
+                        mContext,
+                        mFeatureFlags,
+                        mDialogLaunchAnimator,
+                        mActivityStarter,
+                        /* onStartRecordingClicked= */ null);
 
-        assertThat(dialog).isInstanceOf(ScreenRecordPermissionDialog.class);
+        assertThat(dialog).isSameInstanceAs(mDialogFactory.mLastCreatedDialog);
+        assertThat(mDialogFactory.mLastDelegate)
+                .isInstanceOf(ScreenRecordPermissionDialogDelegate.class);
     }
 
     @Test
@@ -273,4 +305,34 @@
                         /* hostUid= */ myUid(),
                         SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER);
     }
+
+    private static class TestSystemUIDialogFactory extends SystemUIDialog.Factory {
+
+        @Nullable private DialogDelegate<SystemUIDialog> mLastDelegate;
+        @Nullable private SystemUIDialog mLastCreatedDialog;
+
+        TestSystemUIDialogFactory(
+                Context context,
+                FeatureFlags featureFlags,
+                SystemUIDialogManager systemUIDialogManager,
+                SysUiState sysUiState,
+                BroadcastDispatcher broadcastDispatcher,
+                DialogLaunchAnimator dialogLaunchAnimator) {
+            super(
+                    context,
+                    featureFlags,
+                    systemUIDialogManager,
+                    sysUiState,
+                    broadcastDispatcher,
+                    dialogLaunchAnimator);
+        }
+
+        @Override
+        public SystemUIDialog create(DialogDelegate<SystemUIDialog> delegate) {
+            SystemUIDialog dialog = super.create(delegate);
+            mLastDelegate = delegate;
+            mLastCreatedDialog = dialog;
+            return dialog;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
similarity index 80%
rename from packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index fd38139..c848287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -23,16 +23,22 @@
 import android.view.View
 import android.widget.Spinner
 import androidx.test.filters.SmallTest
+import com.android.systemui.Dependency
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
+import com.android.systemui.model.SysUiState
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -50,7 +56,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class ScreenRecordPermissionDialogTest : SysuiTestCase() {
+class ScreenRecordPermissionDialogDelegateTest : SysuiTestCase() {
 
     @Mock private lateinit var starter: ActivityStarter
     @Mock private lateinit var controller: RecordingController
@@ -59,15 +65,23 @@
     @Mock private lateinit var onStartRecordingClicked: Runnable
     @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
 
-    private lateinit var dialog: ScreenRecordPermissionDialog
+    private lateinit var dialog: SystemUIDialog
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        dialog =
-            ScreenRecordPermissionDialog(
+        val systemUIDialogFactory =
+            SystemUIDialog.Factory(
                 context,
+                Dependency.get(FeatureFlags::class.java),
+                Dependency.get(SystemUIDialogManager::class.java),
+                Dependency.get(SysUiState::class.java),
+                Dependency.get(BroadcastDispatcher::class.java),
+                Dependency.get(DialogLaunchAnimator::class.java),
+            )
+        val delegate =
+            ScreenRecordPermissionDialogDelegate(
                 UserHandle.of(0),
                 TEST_HOST_UID,
                 controller,
@@ -76,20 +90,21 @@
                 onStartRecordingClicked,
                 mediaProjectionMetricsLogger,
             )
-        dialog.onCreate(null)
+        dialog = systemUIDialogFactory.create(delegate)
+        delegate.onCreate(dialog, savedInstanceState = null)
         whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
     }
 
     @After
     fun teardown() {
         if (::dialog.isInitialized) {
-            dialog.dismiss()
+            dismissDialog()
         }
     }
 
     @Test
     fun testShowDialog_partialScreenSharingEnabled_optionsSpinnerIsVisible() {
-        dialog.show()
+        showDialog()
 
         val visibility = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner).visibility
         assertThat(visibility).isEqualTo(View.VISIBLE)
@@ -97,7 +112,7 @@
 
     @Test
     fun testShowDialog_singleAppSelected_showTapsIsGone() {
-        dialog.show()
+        showDialog()
         onSpinnerItemSelected(SINGLE_APP)
 
         val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
@@ -106,7 +121,7 @@
 
     @Test
     fun testShowDialog_entireScreenSelected_showTapsIsVisible() {
-        dialog.show()
+        showDialog()
         onSpinnerItemSelected(ENTIRE_SCREEN)
 
         val visibility = dialog.requireViewById<View>(R.id.show_taps).visibility
@@ -115,7 +130,7 @@
 
     @Test
     fun startClicked_singleAppSelected_passesHostUidToAppSelector() {
-        dialog.show()
+        showDialog()
         onSpinnerItemSelected(SINGLE_APP)
 
         clickOnStart()
@@ -128,14 +143,14 @@
 
     @Test
     fun showDialog_dialogIsShowing() {
-        dialog.show()
+        showDialog()
 
         assertThat(dialog.isShowing).isTrue()
     }
 
     @Test
     fun showDialog_singleAppIsDefault() {
-        dialog.show()
+        showDialog()
 
         val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_spinner)
         val singleApp = context.getString(R.string.screen_share_permission_dialog_option_single_app)
@@ -144,7 +159,7 @@
 
     @Test
     fun showDialog_cancelClicked_dialogIsDismissed() {
-        dialog.show()
+        showDialog()
 
         clickOnCancel()
 
@@ -153,7 +168,7 @@
 
     @Test
     fun showDialog_cancelClickedMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
-        dialog.show()
+        showDialog()
 
         clickOnCancel()
         clickOnCancel()
@@ -163,16 +178,22 @@
 
     @Test
     fun dismissDialog_dismissCalledMultipleTimes_projectionRequestCancelledIsLoggedOnce() {
-        dialog.show()
+        showDialog()
 
-        TestableLooper.get(this).runWithLooper {
-            dialog.dismiss()
-            dialog.dismiss()
-        }
+        dismissDialog()
+        dismissDialog()
 
         verify(mediaProjectionMetricsLogger).notifyProjectionRequestCancelled(TEST_HOST_UID)
     }
 
+    private fun showDialog() {
+        dialog.show()
+    }
+
+    private fun dismissDialog() {
+        dialog.dismiss()
+    }
+
     private fun clickOnCancel() {
         dialog.requireViewById<View>(android.R.id.button2).performClick()
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
index 799bb40..2cb17b5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalRepository.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.communal.data.repository
 
 import com.android.systemui.communal.shared.model.CommunalSceneKey
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 
 /** Fake implementation of [CommunalRepository]. */
@@ -16,4 +17,11 @@
     fun setIsCommunalEnabled(value: Boolean) {
         isCommunalEnabled = value
     }
+
+    private val _isCommunalHubShowing: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val isCommunalHubShowing: Flow<Boolean> = _isCommunalHubShowing
+
+    fun setIsCommunalHubShowing(isCommunalHubShowing: Boolean) {
+        _isCommunalHubShowing.value = isCommunalHubShowing
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
new file mode 100644
index 0000000..6c3882f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorFactory.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import android.appwidget.AppWidgetHost
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.smartspace.data.repository.FakeSmartspaceRepository
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.TestScope
+
+object CommunalInteractorFactory {
+
+    @JvmOverloads
+    @JvmStatic
+    fun create(
+        testScope: TestScope = TestScope(),
+        communalRepository: FakeCommunalRepository = FakeCommunalRepository(),
+        widgetRepository: FakeCommunalWidgetRepository = FakeCommunalWidgetRepository(),
+        smartspaceRepository: FakeSmartspaceRepository = FakeSmartspaceRepository(),
+        tutorialRepository: FakeCommunalTutorialRepository = FakeCommunalTutorialRepository(),
+        appWidgetHost: AppWidgetHost = mock(),
+    ): WithDependencies {
+        val withDeps =
+            CommunalTutorialInteractorFactory.create(
+                testScope = testScope,
+                communalTutorialRepository = tutorialRepository,
+                communalRepository = communalRepository,
+            )
+        return WithDependencies(
+            communalRepository,
+            widgetRepository,
+            smartspaceRepository,
+            tutorialRepository,
+            withDeps.keyguardRepository,
+            withDeps.keyguardInteractor,
+            withDeps.communalTutorialInteractor,
+            appWidgetHost,
+            CommunalInteractor(
+                communalRepository,
+                widgetRepository,
+                smartspaceRepository,
+                withDeps.communalTutorialInteractor,
+                appWidgetHost,
+            ),
+        )
+    }
+
+    data class WithDependencies(
+        val communalRepository: FakeCommunalRepository,
+        val widgetRepository: FakeCommunalWidgetRepository,
+        val smartspaceRepository: FakeSmartspaceRepository,
+        val tutorialRepository: FakeCommunalTutorialRepository,
+        val keyguardRepository: FakeKeyguardRepository,
+        val keyguardInteractor: KeyguardInteractor,
+        val tutorialInteractor: CommunalTutorialInteractor,
+        val appWidgetHost: AppWidgetHost,
+        val communalInteractor: CommunalInteractor,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.kt
new file mode 100644
index 0000000..e5cadab
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorFactory.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import com.android.systemui.communal.data.repository.FakeCommunalRepository
+import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import kotlinx.coroutines.test.TestScope
+
+object CommunalTutorialInteractorFactory {
+
+    @JvmOverloads
+    @JvmStatic
+    fun create(
+        testScope: TestScope,
+        communalTutorialRepository: FakeCommunalTutorialRepository =
+            FakeCommunalTutorialRepository(),
+        communalRepository: FakeCommunalRepository =
+            FakeCommunalRepository(isCommunalEnabled = true),
+        keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
+        keyguardInteractor: KeyguardInteractor =
+            KeyguardInteractorFactory.create(
+                    repository = keyguardRepository,
+                )
+                .keyguardInteractor
+    ): WithDependencies {
+        return WithDependencies(
+            testScope = testScope,
+            communalRepository = communalRepository,
+            communalTutorialRepository = communalTutorialRepository,
+            keyguardRepository = keyguardRepository,
+            keyguardInteractor = keyguardInteractor,
+            communalTutorialInteractor =
+                CommunalTutorialInteractor(
+                    testScope.backgroundScope,
+                    communalTutorialRepository,
+                    keyguardInteractor,
+                    communalRepository,
+                )
+        )
+    }
+
+    data class WithDependencies(
+        val testScope: TestScope,
+        val communalRepository: FakeCommunalRepository,
+        val communalTutorialRepository: FakeCommunalTutorialRepository,
+        val keyguardRepository: FakeKeyguardRepository,
+        val keyguardInteractor: KeyguardInteractor,
+        val communalTutorialInteractor: CommunalTutorialInteractor,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index f97d6b3..14c4b86 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -36,8 +36,8 @@
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.communal.data.repository.FakeCommunalRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractorFactory
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
 import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -104,9 +104,6 @@
     }
 
     val communalRepository: FakeCommunalRepository by lazy { FakeCommunalRepository() }
-    private val communalWidgetRepository: FakeCommunalWidgetRepository by lazy {
-        FakeCommunalWidgetRepository()
-    }
     val keyguardRepository: FakeKeyguardRepository by lazy { FakeKeyguardRepository() }
     val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
 
@@ -194,10 +191,10 @@
     }
 
     fun communalInteractor(): CommunalInteractor {
-        return CommunalInteractor(
-            communalRepository = communalRepository,
-            widgetRepository = communalWidgetRepository,
-        )
+        return CommunalInteractorFactory.create(
+                communalRepository = communalRepository,
+            )
+            .communalInteractor
     }
 
     fun bouncerInteractor(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
new file mode 100644
index 0000000..c8013ef
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/smartspace/data/repository/FakeSmartspaceRepository.kt
@@ -0,0 +1,21 @@
+package com.android.systemui.smartspace.data.repository
+
+import android.app.smartspace.SmartspaceTarget
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeSmartspaceRepository(
+    smartspaceRemoteViewsEnabled: Boolean = true,
+) : SmartspaceRepository {
+
+    override val isSmartspaceRemoteViewsEnabled = smartspaceRemoteViewsEnabled
+
+    private val _lockscreenSmartspaceTargets: MutableStateFlow<List<SmartspaceTarget>> =
+        MutableStateFlow(emptyList())
+    override val lockscreenSmartspaceTargets: Flow<List<SmartspaceTarget>> =
+        _lockscreenSmartspaceTargets
+
+    fun setLockscreenSmartspaceTargets(targets: List<SmartspaceTarget>) {
+        _lockscreenSmartspaceTargets.value = targets
+    }
+}
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 25814ec..9cda267 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -59,8 +59,11 @@
     AppNotResponding anr = 4;
     JavaCrash java_crash = 5;
     NativeCrash native_crash = 6;
+    SystemServerStarted system_server_started = 7;
   }
 
+  message SystemServerStarted {}
+
   message Watchdog {
     // The watchdog subject.
     // Required.
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index d0e442e..5c9bf18 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -32,3 +32,14 @@
     host_supported: true,
     visibility: ["//visibility:public"],
 }
+
+java_library {
+    name: "ravenwood-junit",
+    srcs: ["junit-src/**/*.java"],
+    libs: [
+        "junit",
+    ],
+    sdk_version: "core_current",
+    host_supported: true,
+    visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
new file mode 100644
index 0000000..0aac084
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/IgnoreUnderRavenwood.java
@@ -0,0 +1,33 @@
+/*
+ * 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.platform.test.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface IgnoreUnderRavenwood {
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
new file mode 100644
index 0000000..a6b3f66
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -0,0 +1,50 @@
+/*
+ * 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.platform.test.ravenwood;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+
+import org.junit.Assume;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * THIS RULE IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
+ * QUESTIONS ABOUT IT.
+ *
+ * @hide
+ */
+public class RavenwoodRule implements TestRule {
+    public boolean isUnderRavenwood() {
+        // TODO: give ourselves a better environment signal
+        return System.getProperty("java.class.path").contains("ravenwood");
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
+                    Assume.assumeFalse(isUnderRavenwood());
+                }
+                base.evaluate();
+            }
+        };
+    }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a14f3fe..4e49c6e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -199,6 +199,7 @@
         "biometrics_flags_lib",
         "am_flags_lib",
         "com_android_wm_shell_flags_lib",
+        "android.app.flags-aconfig-java"
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 4f32220..065a447 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1421,7 +1421,7 @@
             @UserIdInt int userId);
 
     /**
-     * Sends the PACKAGE_RESTARTED broadcast on the package manager handler thread.
+     * Sends the PACKAGE_RESTARTED broadcast.
      */
     public abstract void sendPackageRestartedBroadcast(@NonNull String packageName,
             int uid, @Intent.Flags int flags);
@@ -1431,4 +1431,10 @@
      */
     public abstract ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(
             int userId);
+
+    /**
+     * Sends the ACTION_PACKAGE_DATA_CLEARED broadcast.
+     */
+    public abstract void sendPackageDataClearedBroadcast(@NonNull String packageName,
+            int uid, int userId, boolean isRestore, boolean isInstantApp);
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 514be15..a0a5f99c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3634,30 +3634,10 @@
                     }
 
                     if (succeeded) {
-                        final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
-                                Uri.fromParts("package", packageName, null /* fragment */));
-                        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
-                                | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                        intent.putExtra(Intent.EXTRA_UID,
-                                (appInfo != null) ? appInfo.uid : INVALID_UID);
-                        intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
-                        if (isRestore) {
-                            intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
-                        }
-                        if (isInstantApp) {
-                            intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        }
-                        final int[] visibilityAllowList = mPackageManagerInt.getVisibilityAllowList(
-                                packageName, resolvedUserId);
 
-                        broadcastIntentInPackage("android", null /* featureId */,
-                                SYSTEM_UID, uid, pid, intent, null /* resolvedType */,
-                                null /* resultToApp */, null /* resultTo */, 0 /* resultCode */,
-                                null /* resultData */, null /* resultExtras */,
-                                isInstantApp ? permission.ACCESS_INSTANT_APPS : null,
-                                null /* bOptions */, false /* serialized */, false /* sticky */,
-                                resolvedUserId, BackgroundStartPrivileges.NONE,
-                                visibilityAllowList);
+                        mPackageManagerInt.sendPackageDataClearedBroadcast(packageName,
+                                ((appInfo != null) ? appInfo.uid : INVALID_UID), resolvedUserId,
+                                isRestore, isInstantApp);
                     }
 
                     if (observer != null) {
@@ -4178,29 +4158,7 @@
             flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY
                     | Intent.FLAG_RECEIVER_FOREGROUND;
         }
-        if (android.content.pm.Flags.stayStopped()) {
-            // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
-            mPackageManagerInt.sendPackageRestartedBroadcast(packageName,
-                    uid, flags);
-        } else {
-            Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
-                    Uri.fromParts("package", packageName, null));
-            intent.addFlags(flags);
-            final int userId = UserHandle.getUserId(uid);
-            final int[] broadcastAllowList =
-                    getPackageManagerInternal().getVisibilityAllowList(packageName, userId);
-            intent.putExtra(Intent.EXTRA_UID, uid);
-            intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-            broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
-                    null /* callerFeatureId */, intent, null /* resolvedType */,
-                    null /* resultToApp */, null /* resultTo */,
-                    0 /* resultCode */, null /* resultData */, null /* resultExtras */,
-                    null /* requiredPermissions */, null /* excludedPermissions */,
-                    null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
-                    false /* sticky */, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
-                    Binder.getCallingPid(), userId, BackgroundStartPrivileges.NONE,
-                    broadcastAllowList, null /* filterExtrasForReceiver */);
-        }
+        mPackageManagerInt.sendPackageRestartedBroadcast(packageName, uid, flags);
     }
 
     private void cleanupDisabledPackageComponentsLocked(
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index e6cdbb5..599d998 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -166,6 +166,7 @@
         "safety_center",
         "sensors",
         "system_performance",
+        "system_sw_usb",
         "test_suites",
         "text",
         "threadnetwork",
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index ab480e8..0814375 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -31,6 +31,7 @@
 import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
 import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
 import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
 import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
 
 import java.io.File;
@@ -141,6 +142,13 @@
         return System.currentTimeMillis();
     }
 
+    /** Logs when system server started. */
+    public void logSystemServerStarted() {
+        CriticalEventProto event = new CriticalEventProto();
+        event.setSystemServerStarted(new SystemServerStarted());
+        log(event);
+    }
+
     /** Logs a watchdog. */
     public void logWatchdog(String subject, UUID uuid) {
         Watchdog watchdog = new Watchdog();
diff --git a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
index b129dd0..d61e7fb 100644
--- a/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
+++ b/services/core/java/com/android/server/media/BluetoothProfileMonitor.java
@@ -47,21 +47,10 @@
     @Nullable
     private BluetoothLeAudio mLeAudioProfile;
 
-    @Nullable
-    private OnProfileChangedListener mOnProfileChangedListener;
-
     BluetoothProfileMonitor(@NonNull Context context,
             @NonNull BluetoothAdapter bluetoothAdapter) {
-        Objects.requireNonNull(context);
-        Objects.requireNonNull(bluetoothAdapter);
-
-        mContext = context;
-        mBluetoothAdapter = bluetoothAdapter;
-    }
-
-    /* package */ synchronized void setOnProfileChangedListener(
-            @NonNull OnProfileChangedListener listener) {
-        mOnProfileChangedListener = listener;
+        mContext = Objects.requireNonNull(context);
+        mBluetoothAdapter = Objects.requireNonNull(bluetoothAdapter);
     }
 
     /* package */ void start() {
@@ -115,15 +104,9 @@
         }
     }
 
-    /* package */ interface OnProfileChangedListener {
-        void onProfileChange(int profile);
-    }
-
     private final class ProfileListener implements BluetoothProfile.ServiceListener {
         @Override
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
-            OnProfileChangedListener listener;
-
             synchronized (BluetoothProfileMonitor.this) {
                 switch (profile) {
                     case BluetoothProfile.A2DP:
@@ -135,22 +118,12 @@
                     case BluetoothProfile.LE_AUDIO:
                         mLeAudioProfile = (BluetoothLeAudio) proxy;
                         break;
-                    default:
-                        return;
                 }
-
-                listener = mOnProfileChangedListener;
-            }
-
-            if (listener != null) {
-                listener.onProfileChange(profile);
             }
         }
 
         @Override
         public void onServiceDisconnected(int profile) {
-            OnProfileChangedListener listener;
-
             synchronized (BluetoothProfileMonitor.this) {
                 switch (profile) {
                     case BluetoothProfile.A2DP:
@@ -162,17 +135,8 @@
                     case BluetoothProfile.LE_AUDIO:
                         mLeAudioProfile = null;
                         break;
-                    default:
-                        return;
                 }
-
-                listener = mOnProfileChangedListener;
-            }
-
-            if (listener != null) {
-                listener.onProfileChange(profile);
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 71562dc..9802adf 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -26,6 +26,7 @@
 
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
+import android.app.Flags;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
@@ -670,14 +671,37 @@
         if (rule.enabled != automaticZenRule.isEnabled()) {
             rule.snoozing = false;
         }
+        if (Flags.modesApi()) {
+            rule.allowManualInvocation = automaticZenRule.isManualInvocationAllowed();
+            rule.iconResId = automaticZenRule.getIconResId();
+            rule.triggerDescription = automaticZenRule.getTriggerDescription();
+            rule.type = automaticZenRule.getType();
+        }
     }
 
     protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
-        AutomaticZenRule azr =  new AutomaticZenRule(rule.name, rule.component,
-                rule.configurationActivity,
-                rule.conditionId, rule.zenPolicy,
-                NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
-                rule.enabled, rule.creationTime);
+        AutomaticZenRule azr;
+        if (Flags.modesApi()) {
+            azr = new AutomaticZenRule.Builder(rule.name, rule.conditionId)
+                    .setManualInvocationAllowed(rule.allowManualInvocation)
+                    .setCreationTime(rule.creationTime)
+                    .setIconResId(rule.iconResId)
+                    .setType(rule.type)
+                    .setZenPolicy(rule.zenPolicy)
+                    .setEnabled(rule.enabled)
+                    .setInterruptionFilter(
+                            NotificationManager.zenModeToInterruptionFilter(rule.zenMode))
+                    .setOwner(rule.component)
+                    .setConfigurationActivity(rule.configurationActivity)
+                    .setTriggerDescription(rule.triggerDescription)
+                    .build();
+        } else {
+            azr = new AutomaticZenRule(rule.name, rule.component,
+                    rule.configurationActivity,
+                    rule.conditionId, rule.zenPolicy,
+                    NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
+                    rule.enabled, rule.creationTime);
+        }
         azr.setPackageName(rule.pkg);
         return azr;
     }
@@ -713,6 +737,9 @@
                 newRule.zenMode = zenMode;
                 newRule.conditionId = conditionId;
                 newRule.enabler = caller;
+                if (Flags.modesApi()) {
+                    newRule.allowManualInvocation = true;
+                }
                 newConfig.manualRule = newRule;
             }
             setConfigLocked(newConfig, reason, null, setRingerMode, callingUid,
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index e367609..7b35589 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -100,6 +100,22 @@
         mAppsFilter = injector.getAppsFilter();
     }
 
+    /**
+     * Sends a broadcast to registered clients on userId for the given Intent.
+     */
+    void sendPackageBroadcastWithIntent(Intent intent, int userId, boolean isInstantApp,
+            @Intent.Flags int flags,
+            int[] visibilityAllowList,
+            final IIntentReceiver finishedReceiver,
+            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+            @Nullable Bundle bOptions) {
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
+        SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+        broadcastAllowList.put(userId, visibilityAllowList);
+        broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
+                filterExtrasForReceiver, bOptions);
+    }
+
     void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
             final int[] userIds, int[] instantUserIds,
@@ -152,8 +168,6 @@
         for (int userId : userIds) {
             final Intent intent = new Intent(action,
                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
-            final String[] requiredPermissions =
-                    isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
             if (extras != null) {
                 intent.putExtras(extras);
             }
@@ -172,30 +186,41 @@
             }
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
-            if (DEBUG_BROADCASTS) {
-                RuntimeException here = new RuntimeException("here");
-                here.fillInStackTrace();
-                Slog.d(TAG, "Sending to user " + userId + ": "
-                        + intent.toShortString(false, true, false, false)
-                        + " " + intent.getExtras(), here);
-            }
-            final boolean ordered;
-            if (mAmInternal.isModernQueueEnabled()) {
-                // When the modern broadcast stack is enabled, deliver all our
-                // broadcasts as unordered, since the modern stack has better
-                // support for sequencing cold-starts, and it supports
-                // delivering resultTo for non-ordered broadcasts
-                ordered = false;
-            } else {
-                ordered = (finishedReceiver != null);
-            }
-            mAmInternal.broadcastIntent(
-                    intent, finishedReceiver, requiredPermissions, ordered, userId,
-                    broadcastAllowList == null ? null : broadcastAllowList.get(userId),
+            broadcastIntent(intent, finishedReceiver, isInstantApp, userId, broadcastAllowList,
                     filterExtrasForReceiver, bOptions);
         }
     }
 
+
+    private void broadcastIntent(Intent intent, IIntentReceiver finishedReceiver,
+            boolean isInstantApp, int userId, @Nullable SparseArray<int[]> broadcastAllowList,
+            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+            @Nullable Bundle bOptions) {
+        final String[] requiredPermissions =
+                isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+        if (DEBUG_BROADCASTS) {
+            RuntimeException here = new RuntimeException("here");
+            here.fillInStackTrace();
+            Slog.d(TAG, "Sending to user " + userId + ": "
+                    + intent.toShortString(false, true, false, false)
+                    + " " + intent.getExtras(), here);
+        }
+        final boolean ordered;
+        if (mAmInternal.isModernQueueEnabled()) {
+            // When the modern broadcast stack is enabled, deliver all our
+            // broadcasts as unordered, since the modern stack has better
+            // support for sequencing cold-starts, and it supports
+            // delivering resultTo for non-ordered broadcasts
+            ordered = false;
+        } else {
+            ordered = (finishedReceiver != null);
+        }
+        mAmInternal.broadcastIntent(
+                intent, finishedReceiver, requiredPermissions, ordered, userId,
+                broadcastAllowList == null ? null : broadcastAllowList.get(userId),
+                filterExtrasForReceiver, bOptions);
+    }
+
     void sendResourcesChangedBroadcast(@NonNull Computer snapshot,
                                        boolean mediaStatus,
                                        boolean replacing,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e5f7962..434c00a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6972,14 +6972,50 @@
             final Bundle extras = new Bundle();
             extras.putInt(Intent.EXTRA_UID, uid);
             extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
-            extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
-            mHandler.post(() -> {
+            if (android.content.pm.Flags.stayStopped()) {
+                extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
+                // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
+                mHandler.post(() -> {
+                    mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
+                            packageName, extras,
+                            flags, null, null,
+                            userIds, null, broadcastAllowList, null,
+                            null);
+                });
+            } else {
                 mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
                         packageName, extras,
                         flags, null, null,
                         userIds, null, broadcastAllowList, null,
                         null);
-            });
+            }
+            mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
+                    packageName, extras, userIds, null /* instantUserIds */,
+                    broadcastAllowList, mHandler);
+        }
+
+        @Override
+        public void sendPackageDataClearedBroadcast(@NonNull String packageName,
+                int uid, int userId, boolean isRestore, boolean isInstantApp) {
+            int[] visibilityAllowList =
+                    snapshotComputer().getVisibilityAllowList(packageName, userId);
+            final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
+                    Uri.fromParts("package", packageName, null /* fragment */));
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+                    | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(Intent.EXTRA_UID, uid);
+            intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+            if (isRestore) {
+                intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
+            }
+            if (isInstantApp) {
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+            }
+            mBroadcastHelper.sendPackageBroadcastWithIntent(intent, userId, isInstantApp,
+                    0 /* flags */, visibilityAllowList, null /* finishedReceiver */,
+                    null /* filterExtrasForReceiver */, null /* bOptions */);
+            mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId,
+                    visibilityAllowList, mHandler);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
index 1f12c88..fa9409f 100644
--- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java
@@ -159,16 +159,25 @@
             }
 
             if (ArrayUtils.isEmpty(instantUserIds)) {
-                doNotifyCallbacks(
+                doNotifyCallbacksByAction(
                         action, pkg, extras, resolvedUserIds, broadcastAllowList, handler);
             } else {
-                doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList, handler);
+                doNotifyCallbacksByAction(action, pkg, extras, instantUserIds, broadcastAllowList,
+                        handler);
             }
         } catch (RemoteException e) {
             // do nothing
         }
     }
 
+    void notifyPackageMonitorWithIntent(Intent intent,
+            int userId, int[] broadcastAllowList, Handler handler) {
+        if (!isAllowedCallbackAction(intent.getAction())) {
+            return;
+        }
+        doNotifyCallbacksByIntent(intent, userId, broadcastAllowList, handler);
+    }
+
     private static boolean isAllowedCallbackAction(String action) {
         return TextUtils.equals(action, Intent.ACTION_PACKAGE_ADDED)
                 || TextUtils.equals(action, Intent.ACTION_PACKAGE_REMOVED)
@@ -177,10 +186,21 @@
                 || TextUtils.equals(action, Intent.ACTION_PACKAGES_SUSPENDED)
                 || TextUtils.equals(action, Intent.ACTION_PACKAGES_UNSUSPENDED)
                 || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE)
-                || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+                || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
+                || TextUtils.equals(action, Intent.ACTION_PACKAGE_DATA_CLEARED)
+                || TextUtils.equals(action, Intent.ACTION_PACKAGE_RESTARTED);
     }
 
-    private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds,
+    private void doNotifyCallbacksByIntent(Intent intent, int userId,
+            int[] broadcastAllowList, Handler handler) {
+        RemoteCallbackList<IRemoteCallback> callbacks;
+        synchronized (mLock) {
+            callbacks = mCallbacks;
+        }
+        doNotifyCallbacks(callbacks, intent, userId, broadcastAllowList, handler);
+    }
+
+    private void doNotifyCallbacksByAction(String action, String pkg, Bundle extras, int[] userIds,
             SparseArray<int[]> broadcastAllowList, Handler handler) {
         RemoteCallbackList<IRemoteCallback> callbacks;
         synchronized (mLock) {
@@ -200,29 +220,33 @@
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
 
             final int[] allowUids =
-                    broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{};
-
-            handler.post(() -> callbacks.broadcast((callback, user) -> {
-                RegisterUser registerUser = (RegisterUser) user;
-                if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
-                        != userId)) {
-                    return;
-                }
-                int registerUid = registerUser.getUid();
-                if (broadcastAllowList != null && registerUid != Process.SYSTEM_UID
-                        && !ArrayUtils.contains(allowUids, registerUid)) {
-                    if (DEBUG) {
-                        Slog.w("PackageMonitorCallbackHelper",
-                                "Skip invoke PackageMonitorCallback for " + action + ", uid "
-                                        + registerUid);
-                    }
-                    return;
-                }
-                invokeCallback(callback, intent);
-            }));
+                    broadcastAllowList != null ? broadcastAllowList.get(userId) : null;
+            doNotifyCallbacks(callbacks, intent, userId, allowUids, handler);
         }
     }
 
+    private void doNotifyCallbacks(RemoteCallbackList<IRemoteCallback> callbacks,
+            Intent intent, int userId, int[] allowUids, Handler handler) {
+        handler.post(() -> callbacks.broadcast((callback, user) -> {
+            RegisterUser registerUser = (RegisterUser) user;
+            if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId()
+                    != userId)) {
+                return;
+            }
+            int registerUid = registerUser.getUid();
+            if (allowUids != null && registerUid != Process.SYSTEM_UID
+                    && !ArrayUtils.contains(allowUids, registerUid)) {
+                if (DEBUG) {
+                    Slog.w("PackageMonitorCallbackHelper",
+                            "Skip invoke PackageMonitorCallback for " + intent.getAction()
+                                    + ", uid " + registerUid);
+                }
+                return;
+            }
+            invokeCallback(callback, intent);
+        }));
+    }
+
     private void invokeCallback(IRemoteCallback callback, Intent intent) {
         try {
             Bundle bundle = new Bundle();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 45ca690..cf1036c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -549,6 +549,7 @@
     int mLidNavigationAccessibility;
     int mShortPressOnPowerBehavior;
     private boolean mShouldEarlyShortPressOnPower;
+    private boolean mShouldEarlyShortPressOnStemPrimary;
     int mLongPressOnPowerBehavior;
     long mLongPressOnPowerAssistantTimeoutMs;
     int mVeryLongPressOnPowerBehavior;
@@ -2748,6 +2749,9 @@
 
         @Override
         void onPress(long downTime) {
+            if (mShouldEarlyShortPressOnStemPrimary) {
+                return;
+            }
             stemPrimaryPress(1 /*count*/);
         }
 
@@ -2760,6 +2764,13 @@
         void onMultiPress(long downTime, int count) {
             stemPrimaryPress(count);
         }
+
+        @Override
+        void onKeyUp(long eventTime, int count) {
+            if (mShouldEarlyShortPressOnStemPrimary && count == 1) {
+                stemPrimaryPress(1 /*pressCount*/);
+            }
+        }
     }
 
     private void initSingleKeyGestureRules(Looper looper) {
@@ -2929,6 +2940,8 @@
             mShouldEarlyShortPressOnPower =
                     mContext.getResources()
                             .getBoolean(com.android.internal.R.bool.config_shortPressEarlyOnPower);
+            mShouldEarlyShortPressOnStemPrimary = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_shortPressEarlyOnStemPrimary);
 
             mStylusButtonsEnabled = Settings.Secure.getIntForUser(resolver,
                     Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 3dbd2f8..6a0fe90 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -395,7 +395,8 @@
 
     @VisibleForTesting
     int getDesiredScreenPolicyLocked(boolean quiescent, boolean dozeAfterScreenOff,
-            boolean bootCompleted, boolean screenBrightnessBoostInProgress) {
+            boolean bootCompleted, boolean screenBrightnessBoostInProgress,
+            boolean brightWhenDozing) {
         final int wakefulness = getWakefulnessLocked();
         final int wakeLockSummary = getWakeLockSummaryLocked();
         if (wakefulness == WAKEFULNESS_ASLEEP || quiescent) {
@@ -407,8 +408,12 @@
             if (dozeAfterScreenOff) {
                 return DisplayPowerRequest.POLICY_OFF;
             }
+            if (brightWhenDozing) {
+                return DisplayPowerRequest.POLICY_BRIGHT;
+            }
             // Fall through and preserve the current screen policy if not configured to
-            // doze after screen off.  This causes the screen off transition to be skipped.
+            // bright when dozing or doze after screen off.  This causes the screen off transition
+            // to be skipped.
         }
 
         if ((wakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0
@@ -429,9 +434,10 @@
             boolean boostScreenBrightness, int dozeScreenState, float dozeScreenBrightness,
             boolean overrideDrawWakeLock, PowerSaveState powerSaverState, boolean quiescent,
             boolean dozeAfterScreenOff, boolean bootCompleted,
-            boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity) {
+            boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity,
+            boolean brightWhenDozing) {
         mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked(quiescent, dozeAfterScreenOff,
-                bootCompleted, screenBrightnessBoostInProgress);
+                bootCompleted, screenBrightnessBoostInProgress, brightWhenDozing);
         mDisplayPowerRequest.screenBrightnessOverride = screenBrightnessOverride;
         mDisplayPowerRequest.useProximitySensor = useProximitySensor;
         mDisplayPowerRequest.boostScreenBrightness = boostScreenBrightness;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 8ce0c72..ec5172f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -546,6 +546,10 @@
     // True if doze should not be started until after the screen off transition.
     private boolean mDozeAfterScreenOff;
 
+    // True if bright policy should be applied when we have entered dozing wakefulness but haven't
+    // started doze component.
+    private boolean mBrightWhenDozingConfig;
+
     // The minimum screen off timeout, in milliseconds.
     private long mMinimumScreenOffTimeoutConfig;
 
@@ -1492,6 +1496,8 @@
                 com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig);
         mDozeAfterScreenOff = resources.getBoolean(
                 com.android.internal.R.bool.config_dozeAfterScreenOffByDefault);
+        mBrightWhenDozingConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_brightWhenDozing);
         mMinimumScreenOffTimeoutConfig = resources.getInteger(
                 com.android.internal.R.integer.config_minimumScreenOffTimeout);
         mMaximumScreenDimDurationConfig = resources.getInteger(
@@ -3560,7 +3566,8 @@
                                         .getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS)
                                 : new PowerSaveState.Builder().build(),
                         sQuiescent, mDozeAfterScreenOff, mBootCompleted,
-                        mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity);
+                        mScreenBrightnessBoostInProgress, mRequestWaitForNegativeProximity,
+                        mBrightWhenDozingConfig);
                 int wakefulness = powerGroup.getWakefulnessLocked();
                 if (DEBUG_SPEW) {
                     Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
@@ -3635,7 +3642,7 @@
     int getDesiredScreenPolicyLocked(int groupId) {
         return mPowerGroups.get(groupId).getDesiredScreenPolicyLocked(sQuiescent,
                 mDozeAfterScreenOff, mBootCompleted,
-                mScreenBrightnessBoostInProgress);
+                mScreenBrightnessBoostInProgress, mBrightWhenDozingConfig);
     }
 
     @VisibleForTesting
@@ -4655,6 +4662,7 @@
             pw.println("  mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
             pw.println("  mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
             pw.println("  mDozeAfterScreenOff=" + mDozeAfterScreenOff);
+            pw.println("  mBrightWhenDozingConfig=" + mBrightWhenDozingConfig);
             pw.println("  mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
             pw.println("  mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
             pw.println("  mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
diff --git a/services/core/java/com/android/server/timedetector/TEST_MAPPING b/services/core/java/com/android/server/timedetector/TEST_MAPPING
index f1bfea7..5c37680 100644
--- a/services/core/java/com/android/server/timedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timedetector/TEST_MAPPING
@@ -1,14 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timedetector."
-        }
-      ]
-    },
-    {
       "name": "CtsTimeTestCases",
       "options": [
         {
@@ -16,5 +8,11 @@
         }
       ]
     }
+  ],
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeServicesTests"
+    }
   ]
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index d88f426..83270f6 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.time.ExternalTimeSuggestion;
 import android.app.time.ITimeDetectorListener;
 import android.app.time.TimeCapabilitiesAndConfig;
@@ -30,7 +29,6 @@
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
 import android.content.Context;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ParcelableException;
@@ -166,8 +164,7 @@
      */
     boolean updateConfiguration(@UserIdInt int userId, @NonNull TimeConfiguration configuration) {
         // Resolve constants like USER_CURRENT to the true user ID as needed.
-        int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, "updateConfiguration", null);
+        int resolvedUserId = mCallerIdentityInjector.resolveUserId(userId, "updateConfiguration");
 
         enforceManageTimeDetectorPermission();
 
@@ -280,11 +277,11 @@
     public TimeState getTimeState() {
         enforceManageTimeDetectorPermission();
 
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeDetectorStrategy.getTimeState();
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -295,11 +292,11 @@
     void setTimeState(@NonNull TimeState timeState) {
         enforceManageTimeDetectorPermission();
 
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             mTimeDetectorStrategy.setTimeState(timeState);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -308,11 +305,11 @@
         enforceManageTimeDetectorPermission();
         Objects.requireNonNull(time);
 
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeDetectorStrategy.confirmTime(time);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -324,13 +321,13 @@
         // This calls suggestManualTime() as the logic is identical, it only differs in the
         // permission required, which is handled on the line above.
         int userId = mCallerIdentityInjector.getCallingUserId();
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             final boolean bypassUserPolicyChecks = false;
             return mTimeDetectorStrategy.suggestManualTime(
                     userId, suggestion, bypassUserPolicyChecks);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -377,11 +374,11 @@
     void clearLatestNetworkTime() {
         enforceSuggestNetworkTimePermission();
 
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             mTimeDetectorStrategy.clearLatestNetworkSuggestion();
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -473,7 +470,7 @@
     void clearNetworkTimeForSystemClockForTests() {
         enforceSuggestNetworkTimePermission();
 
-        final long token = Binder.clearCallingIdentity();
+        final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             // TODO(b/222295093): Remove this condition once we can be sure that all uses of
             //  NtpTrustedTime result in a suggestion being made to the time detector.
@@ -485,7 +482,7 @@
                 mNtpTrustedTime.clearCachedTimeResult();
             }
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
index 1500cfa..1f1d83f 100644
--- a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
+++ b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
@@ -17,6 +17,7 @@
 package com.android.server.timezonedetector;
 
 import android.annotation.UserIdInt;
+import android.app.ActivityManager;
 import android.os.Binder;
 import android.os.UserHandle;
 
@@ -29,6 +30,12 @@
     /** A singleton for the real implementation of {@link CallerIdentityInjector}. */
     CallerIdentityInjector REAL = new Real();
 
+    /**
+     * A {@link ActivityManager#handleIncomingUser} call. This can be used to map the abstract
+     * user ID value USER_CURRENT to the actual user ID.
+     */
+    @UserIdInt int resolveUserId(@UserIdInt int userId, String debugInfo);
+
     /** A {@link UserHandle#getCallingUserId()} call. */
     @UserIdInt int getCallingUserId();
 
@@ -45,6 +52,12 @@
         }
 
         @Override
+        public int resolveUserId(@UserIdInt int userId, String debugName) {
+            return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, false, debugName, null);
+        }
+
+        @Override
         public int getCallingUserId() {
             return UserHandle.getCallingUserId();
         }
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
index 455accd..63dd7b4 100644
--- a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -1,14 +1,6 @@
 {
   "presubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timezonedetector."
-        }
-      ]
-    },
-    {
+   {
       "name": "CtsTimeTestCases",
       "options": [
         {
@@ -21,6 +13,9 @@
   "postsubmit": [
     {
       "name": "CtsLocationTimeZoneManagerHostTest"
+    },
+    {
+      "name": "FrameworksTimeServicesTests"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index dac4bf8..d914544 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.app.time.ITimeZoneDetectorListener;
 import android.app.time.TimeZoneCapabilitiesAndConfig;
 import android.app.time.TimeZoneConfiguration;
@@ -28,7 +27,6 @@
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.content.Context;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -168,8 +166,8 @@
         enforceManageTimeZoneDetectorPermission();
 
         // Resolve constants like USER_CURRENT to the true user ID as needed.
-        int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, "getCapabilitiesAndConfig", null);
+        int resolvedUserId =
+                mCallerIdentityInjector.resolveUserId(userId, "getCapabilitiesAndConfig");
 
         final long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
@@ -190,8 +188,7 @@
     boolean updateConfiguration(
             @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
         // Resolve constants like USER_CURRENT to the true user ID as needed.
-        int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, false, "updateConfiguration", null);
+        int resolvedUserId = mCallerIdentityInjector.resolveUserId(userId, "updateConfiguration");
 
         enforceManageTimeZoneDetectorPermission();
 
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotController.java b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
index 52ab9b8..86be6ba 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotController.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotController.java
@@ -237,22 +237,8 @@
             }
             if (containsFile(code, userId)) {
                 synchronized (mSnapshotPersistQueue.getLock()) {
-                    mSnapshotPersistQueue.sendToQueueLocked(
-                            new SnapshotPersistQueue.WriteQueueItem(mPersistInfoProvider) {
-                                @Override
-                                void write() {
-                                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                                            "load_activity_snapshot");
-                                    final TaskSnapshot snapshot = mSnapshotLoader.loadTask(code,
-                                            userId, false /* loadLowResolutionBitmap */);
-                                    synchronized (mService.getWindowManagerLock()) {
-                                        if (snapshot != null && !ar.finishing) {
-                                            mCache.putSnapshot(ar, snapshot);
-                                        }
-                                    }
-                                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                                }
-                            });
+                    mSnapshotPersistQueue.insertQueueAtFirstLocked(
+                            new LoadActivitySnapshotItem(ar, code, userId, mPersistInfoProvider));
                 }
             }
         }
@@ -273,6 +259,42 @@
         resetTmpFields();
     }
 
+    class LoadActivitySnapshotItem extends SnapshotPersistQueue.WriteQueueItem {
+        private final int mCode;
+        private final int mUserId;
+        private final ActivityRecord mActivityRecord;
+
+        LoadActivitySnapshotItem(@NonNull ActivityRecord ar, int code, int userId,
+                @NonNull PersistInfoProvider persistInfoProvider) {
+            super(persistInfoProvider);
+            mActivityRecord = ar;
+            mCode = code;
+            mUserId = userId;
+        }
+
+        @Override
+        void write() {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                    "load_activity_snapshot");
+            final TaskSnapshot snapshot = mSnapshotLoader.loadTask(mCode,
+                    mUserId, false /* loadLowResolutionBitmap */);
+            synchronized (mService.getWindowManagerLock()) {
+                if (snapshot != null && !mActivityRecord.finishing) {
+                    mCache.putSnapshot(mActivityRecord, snapshot);
+                }
+            }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || getClass() != o.getClass()) return false;
+            final LoadActivitySnapshotItem other = (LoadActivitySnapshotItem) o;
+            return mCode == other.mCode && mUserId == other.mUserId
+                    && mPersistInfoProvider == other.mPersistInfoProvider;
+        }
+    }
+
     void recordSnapshot(ActivityRecord activity) {
         if (shouldDisableSnapshots()) {
             return;
@@ -571,7 +593,8 @@
             for (int i = savedFileCount - 1; i > removeTillIndex; --i) {
                 final UserSavedFile usf = mSavedFilesInOrder.remove(i);
                 if (usf != null) {
-                    mUserSavedFiles.remove(usf.mFileId);
+                    final SparseArray<UserSavedFile> records = getUserFiles(usf.mUserId);
+                    records.remove(usf.mFileId);
                     usfs.add(usf);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 4a5311b..2c49203 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -269,12 +269,27 @@
         }
     }
 
+    /**
+     * Start intent as a package.
+     *
+     * @param uid Make a call as if this UID did.
+     * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
+     * @param intent Intent to start.
+     * @param userId Start the intents on this user.
+     * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
+     */
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, @Nullable String callingFeatureId, Intent intent,
             String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges) {
+            BackgroundStartPrivileges forcedBalByPiSender) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -295,7 +310,7 @@
                 .setUserId(userId)
                 .setInTask(inTask)
                 .setOriginatingPendingIntent(originatingPendingIntent)
-                .setBackgroundStartPrivileges(backgroundStartPrivileges)
+                .setBackgroundStartPrivileges(forcedBalByPiSender)
                 .execute();
     }
 
@@ -310,15 +325,18 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
      */
     final int startActivitiesInPackage(int uid, String callingPackage,
             @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
             IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
             PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges) {
+            BackgroundStartPrivileges forcedBalByPiSender) {
         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
                 callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
-                validateIncomingUser, originatingPendingIntent, backgroundStartPrivileges);
+                validateIncomingUser, originatingPendingIntent, forcedBalByPiSender);
     }
 
     /**
@@ -333,12 +351,15 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
      */
     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges) {
+            BackgroundStartPrivileges forcedBalByPiSender) {
 
         final String reason = "startActivityInPackage";
 
@@ -348,14 +369,14 @@
         // TODO: Switch to user app stacks here.
         return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
                 callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
-                originatingPendingIntent, backgroundStartPrivileges);
+                originatingPendingIntent, forcedBalByPiSender);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
             int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
             int userId, String reason, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges) {
+            BackgroundStartPrivileges forcedBalByPiSender) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -463,7 +484,7 @@
                         // top one as otherwise an activity below might consume it.
                         .setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
                         .setOriginatingPendingIntent(originatingPendingIntent)
-                        .setBackgroundStartPrivileges(backgroundStartPrivileges);
+                        .setBackgroundStartPrivileges(forcedBalByPiSender);
             }
             // Log if the activities to be started have different uids.
             if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 34bf8ed..009b8e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -388,7 +388,7 @@
         WaitResult waitResult;
         int filterCallingUid;
         PendingIntentRecord originatingPendingIntent;
-        BackgroundStartPrivileges backgroundStartPrivileges;
+        BackgroundStartPrivileges forcedBalByPiSender;
 
         final StringBuilder logMessage = new StringBuilder();
 
@@ -451,7 +451,7 @@
             allowPendingRemoteAnimationRegistryLookup = true;
             filterCallingUid = UserHandle.USER_NULL;
             originatingPendingIntent = null;
-            backgroundStartPrivileges = BackgroundStartPrivileges.NONE;
+            forcedBalByPiSender = BackgroundStartPrivileges.NONE;
             errorCallbackToken = null;
         }
 
@@ -494,7 +494,7 @@
                     = request.allowPendingRemoteAnimationRegistryLookup;
             filterCallingUid = request.filterCallingUid;
             originatingPendingIntent = request.originatingPendingIntent;
-            backgroundStartPrivileges = request.backgroundStartPrivileges;
+            forcedBalByPiSender = request.forcedBalByPiSender;
             errorCallbackToken = request.errorCallbackToken;
         }
 
@@ -1106,7 +1106,7 @@
                             realCallingPid,
                             callerApp,
                             request.originatingPendingIntent,
-                            request.backgroundStartPrivileges,
+                            request.forcedBalByPiSender,
                             intent,
                             checkedOptions);
                 balCode = balVerdict.getCode();
@@ -3167,9 +3167,8 @@
         return this;
     }
 
-    ActivityStarter setBackgroundStartPrivileges(
-            BackgroundStartPrivileges backgroundStartPrivileges) {
-        mRequest.backgroundStartPrivileges = backgroundStartPrivileges;
+    ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) {
+        mRequest.forcedBalByPiSender = forcedBalByPiSender;
         return this;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index a2547fd..5604b1a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -215,21 +215,39 @@
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
-     * @param allowBackgroundActivityStart Whether the background activity start should be allowed
-     *        from originatingPendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
      */
     public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
             String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
             String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges);
+            BackgroundStartPrivileges forcedBalByPiSender);
 
+    /**
+     * Start intent as a package.
+     *
+     * @param uid Make a call as if this UID did.
+     * @param realCallingPid PID of the real caller.
+     * @param realCallingUid UID of the real caller.
+     * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
+     * @param intent Intent to start.
+     * @param userId Start the intents on this user.
+     * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
+     */
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, @Nullable String callingFeaturId, Intent intent,
+            String callingPackage, @Nullable String callingFeatureId, Intent intent,
             String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
             boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges);
+            BackgroundStartPrivileges forcedBalByPiSender);
 
     /**
      * Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f462efc..3c56a4e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3859,10 +3859,14 @@
                     Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible");
                     return null;
                 }
+                // Note that if updateCache is true, ActivityRecord#shouldUseAppThemeSnapshot will
+                // be used to decide whether the task is allowed to be captured because that may
+                // be retrieved by recents. While if updateCache is false, the real snapshot will
+                // always be taken and the snapshot won't be put into SnapshotPersister.
                 if (updateCache) {
                     return mWindowManager.mTaskSnapshotController.recordSnapshot(task);
                 } else {
-                    return mWindowManager.mTaskSnapshotController.captureSnapshot(task);
+                    return mWindowManager.mTaskSnapshotController.snapshot(task);
                 }
             }
         } finally {
@@ -5911,12 +5915,12 @@
                 String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
                 String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
                 boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-                BackgroundStartPrivileges backgroundStartPrivileges) {
+                BackgroundStartPrivileges forcedBalByPiSender) {
             assertPackageMatchesCallingUid(callingPackage);
             return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
                     realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
                     resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
-                    backgroundStartPrivileges);
+                    forcedBalByPiSender);
         }
 
         @Override
@@ -5925,13 +5929,13 @@
                 String resolvedType, IBinder resultTo, String resultWho, int requestCode,
                 int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
                 boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-                BackgroundStartPrivileges backgroundStartPrivileges) {
+                BackgroundStartPrivileges forcedBalByPiSender) {
             assertPackageMatchesCallingUid(callingPackage);
             return getActivityStartController().startActivityInPackage(uid, realCallingPid,
                     realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
                     resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
                     reason, validateIncomingUser, originatingPendingIntent,
-                    backgroundStartPrivileges);
+                    forcedBalByPiSender);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 2f9ef50..1b5631f5 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -195,6 +195,10 @@
         return activity != null && packageName.equals(activity.getPackageName());
     }
 
+    /**
+     * @see #checkBackgroundActivityStart(int, int, String, int, int, WindowProcessController,
+     *      PendingIntentRecord, BackgroundStartPrivileges, Intent, ActivityOptions)
+     */
     boolean shouldAbortBackgroundActivityStart(
             int callingUid,
             int callingPid,
@@ -203,13 +207,13 @@
             int realCallingPid,
             WindowProcessController callerApp,
             PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges,
+            BackgroundStartPrivileges forcedBalByPiSender,
             Intent intent,
             ActivityOptions checkedOptions) {
         return checkBackgroundActivityStart(callingUid, callingPid, callingPackage,
                 realCallingUid, realCallingPid,
                 callerApp, originatingPendingIntent,
-                backgroundStartPrivileges, intent, checkedOptions).blocks();
+                forcedBalByPiSender, intent, checkedOptions).blocks();
     }
 
     private class BalState {
@@ -230,7 +234,7 @@
         private final @ActivityManager.ProcessState int mRealCallingUidProcState;
         private final boolean mIsRealCallingUidPersistentSystemProcess;
         private final PendingIntentRecord mOriginatingPendingIntent;
-        private final BackgroundStartPrivileges mBackgroundStartPrivileges;
+        private final BackgroundStartPrivileges mForcedBalByPiSender;
         private final Intent mIntent;
         private final WindowProcessController mCallerApp;
         private final WindowProcessController mRealCallerApp;
@@ -239,7 +243,7 @@
                  int realCallingUid, int realCallingPid,
                  WindowProcessController callerApp,
                  PendingIntentRecord originatingPendingIntent,
-                 BackgroundStartPrivileges backgroundStartPrivileges,
+                 BackgroundStartPrivileges forcedBalByPiSender,
                  Intent intent,
                  ActivityOptions checkedOptions) {
             this.mCallingPackage = callingPackage;
@@ -248,7 +252,7 @@
             mRealCallingUid = realCallingUid;
             mRealCallingPid = realCallingPid;
             mCallerApp = callerApp;
-            mBackgroundStartPrivileges = backgroundStartPrivileges;
+            mForcedBalByPiSender = forcedBalByPiSender;
             mOriginatingPendingIntent = originatingPendingIntent;
             mIntent = intent;
             mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
@@ -344,7 +348,7 @@
                         .append(mIsRealCallingUidPersistentSystemProcess);
                 sb.append("; originatingPendingIntent: ").append(mOriginatingPendingIntent);
             }
-            sb.append("; backgroundStartPrivileges: ").append(mBackgroundStartPrivileges);
+            sb.append("; mForcedBalByPiSender: ").append(mForcedBalByPiSender);
             sb.append("; intent: ").append(mIntent);
             sb.append("; callerApp: ").append(mCallerApp);
             if (isPendingIntent()) {
@@ -422,8 +426,26 @@
     }
 
     /**
-     * @return A code denoting which BAL rule allows an activity to be started,
-     * or {@link #BAL_BLOCK} if the launch should be blocked
+     * Check if a (background) activity start is allowed.
+     *
+     * @param callingUid The UID that wants to start the activity.
+     * @param callingPid The PID that wants to start the activity.
+     * @param callingPackage The package name that wants to start the activity.
+     * @param realCallingUid The UID that actually calls this method (only if this handles a
+     *      PendingIntent, otherwise -1)
+     * @param realCallingPid The PID that actually calls this method (only if this handles a
+     *      *      PendingIntent, otherwise -1)
+     * @param callerApp The process that calls this method (only if not a PendingIntent)
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
+     * @param forcedBalByPiSender If set to allow, the
+     *        PendingIntent's sender will try to force allow background activity starts.
+     *        This is only possible if the sender of the PendingIntent is a system process.
+     * @param intent Intent that should be started.
+     * @param checkedOptions ActivityOptions to allow specific opt-ins/opt outs.
+     *
+     * @return A verdict denoting which BAL rule allows an activity to be started,
+     *        or if the launch should be blocked.
      */
     BalVerdict checkBackgroundActivityStart(
             int callingUid,
@@ -433,7 +455,7 @@
             int realCallingPid,
             WindowProcessController callerApp,
             PendingIntentRecord originatingPendingIntent,
-            BackgroundStartPrivileges backgroundStartPrivileges,
+            BackgroundStartPrivileges forcedBalByPiSender,
             Intent intent,
             ActivityOptions checkedOptions) {
 
@@ -444,7 +466,7 @@
 
         BalState state = new BalState(callingUid, callingPid, callingPackage,
                 realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
-                backgroundStartPrivileges, intent, checkedOptions);
+                forcedBalByPiSender, intent, checkedOptions);
 
         // In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
         // visible window.
@@ -708,12 +730,12 @@
         }
         // if the realCallingUid is a persistent system process, abort if the IntentSender
         // wasn't allowed to start an activity
-        if (state.mIsRealCallingUidPersistentSystemProcess
-                && state.mBackgroundStartPrivileges.allowsBackgroundActivityStarts()) {
+        if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
+                && state.mIsRealCallingUidPersistentSystemProcess) {
             return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
                     /*background*/ false,
                     "realCallingUid is persistent system process AND intent "
-                            + "sender allowed (allowBackgroundActivityStart = true).");
+                            + "sender forced to allow.");
         }
         // don't abort if the realCallingUid is an associated companion app
         if (mService.isAssociatedCompanionApp(
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index d69c5ef..8a7cc67 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1876,7 +1876,8 @@
                 dc.getDisplayPolicy().simulateLayoutDisplay(df);
                 final InsetsState insetsState = df.mInsetsState;
                 final Rect displayFrame = insetsState.getDisplayFrame();
-                final Insets decor = insetsState.calculateInsets(displayFrame, DECOR_TYPES,
+                final Insets decor = insetsState.calculateInsets(displayFrame,
+                        dc.mWmService.mDecorTypes,
                         true /* ignoreVisibility */);
                 final Insets statusBar = insetsState.calculateInsets(displayFrame,
                         Type.statusBars(), true /* ignoreVisibility */);
@@ -1912,15 +1913,6 @@
             }
         }
 
-
-        static final int DECOR_TYPES = Type.displayCutout() | Type.navigationBars();
-
-        /**
-         * The types that may affect display configuration. This excludes cutout because it is
-         * known from display info.
-         */
-        static final int CONFIG_TYPES = Type.statusBars() | Type.navigationBars();
-
         private final DisplayContent mDisplayContent;
         private final Info[] mInfoForRotation = new Info[4];
         final Info mTmpInfo = new Info();
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cd114fc..9d5ddf3 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,6 +30,7 @@
 import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING;
 import static com.android.server.wm.InsetsSourceProviderProto.SERVER_VISIBLE;
 import static com.android.server.wm.InsetsSourceProviderProto.SOURCE;
+import static com.android.server.wm.InsetsSourceProviderProto.SOURCE_WINDOW_STATE;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
 
 import android.annotation.NonNull;
@@ -304,7 +305,7 @@
             return mInsetsHint;
         }
         final WindowState win = mWindowContainer.asWindowState();
-        if (win != null && win.mGivenInsetsPending) {
+        if (win != null && win.mGivenInsetsPending && win.mAttrs.providedInsets == null) {
             return mInsetsHint;
         }
         if (mInsetsHintStale) {
@@ -680,6 +681,9 @@
         proto.write(SERVER_VISIBLE, mServerVisible);
         proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
         proto.write(CONTROLLABLE, mControllable);
+        if (mWindowContainer != null && mWindowContainer.asWindowState() != null) {
+            mWindowContainer.asWindowState().dumpDebug(proto, SOURCE_WINDOW_STATE, logLevel);
+        }
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e82f322..5227a52 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2095,6 +2095,7 @@
             }
 
             final TaskFragment organizedTf = r.getOrganizedTaskFragment();
+            final TaskFragment taskFragment = r.getTaskFragment();
             final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
             if (singleActivity) {
                 rootTask = task;
@@ -2137,7 +2138,11 @@
                         .setIntent(r.intent)
                         .setDeferTaskAppear(true)
                         .setHasBeenVisible(true)
-                        .setWindowingMode(task.getRequestedOverrideWindowingMode())
+                        // In case the activity is in system split screen, or Activity Embedding
+                        // split, we need to animate the PIP Task from the original TaskFragment
+                        // bounds, so also setting the windowing mode, otherwise the bounds may
+                        // be reset to fullscreen.
+                        .setWindowingMode(taskFragment.getWindowingMode())
                         .build();
                 // Establish bi-directional link between the original and pinned task.
                 r.setLastParentBeforePip(launchIntoPipHostActivity);
@@ -2150,7 +2155,7 @@
                 // current bounds.
                 // Use Task#setBoundsUnchecked to skip checking windowing mode as the windowing mode
                 // will be updated later after this is collected in transition.
-                rootTask.setBoundsUnchecked(r.getTaskFragment().getBounds());
+                rootTask.setBoundsUnchecked(taskFragment.getBounds());
 
                 // Move the last recents animation transaction from original task to the new one.
                 if (task.mLastRecentsAnimationTransaction != null) {
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index f4f641f..bffdf54 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -23,7 +23,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.graphics.Bitmap;
 import android.os.Process;
 import android.os.SystemClock;
@@ -33,6 +32,7 @@
 import android.window.TaskSnapshot;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
@@ -100,7 +100,7 @@
         }
     }
 
-    @TestApi
+    @VisibleForTesting
     void waitForQueueEmpty() {
         while (true) {
             synchronized (mLock) {
@@ -112,9 +112,20 @@
         }
     }
 
-    @GuardedBy("mLock")
-    void sendToQueueLocked(WriteQueueItem item) {
-        mWriteQueue.offer(item);
+    @VisibleForTesting
+    int peekQueueSize() {
+        synchronized (mLock) {
+            return mWriteQueue.size();
+        }
+    }
+
+    private void addToQueueInternal(WriteQueueItem item, boolean insertToFront) {
+        mWriteQueue.removeFirstOccurrence(item);
+        if (insertToFront) {
+            mWriteQueue.addFirst(item);
+        } else {
+            mWriteQueue.addLast(item);
+        }
         item.onQueuedLocked();
         ensureStoreQueueDepthLocked();
         if (!mPaused) {
@@ -123,6 +134,16 @@
     }
 
     @GuardedBy("mLock")
+    void sendToQueueLocked(WriteQueueItem item) {
+        addToQueueInternal(item, false /* insertToFront */);
+    }
+
+    @GuardedBy("mLock")
+    void insertQueueAtFirstLocked(WriteQueueItem item) {
+        addToQueueInternal(item, true /* insertToFront */);
+    }
+
+    @GuardedBy("mLock")
     private void ensureStoreQueueDepthLocked() {
         while (mStoreQueueItems.size() > MAX_STORE_QUEUE_DEPTH) {
             final StoreWriteQueueItem item = mStoreQueueItems.poll();
@@ -235,6 +256,8 @@
         @GuardedBy("mLock")
         @Override
         void onQueuedLocked() {
+            // Remove duplicate request.
+            mStoreQueueItems.remove(this);
             mStoreQueueItems.offer(this);
         }
 
@@ -358,6 +381,14 @@
 
             return true;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || getClass() != o.getClass()) return false;
+            final StoreWriteQueueItem other = (StoreWriteQueueItem) o;
+            return mId == other.mId && mUserId == other.mUserId
+                    && mPersistInfoProvider == other.mPersistInfoProvider;
+        }
     }
 
     DeleteWriteQueueItem createDeleteWriteQueueItem(int id, int userId,
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 00f2b89..197edc3 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -274,11 +274,6 @@
     boolean mClearedForReorderActivityToFront;
 
     /**
-     * Whether the TaskFragment surface is managed by a system {@link TaskFragmentOrganizer}.
-     */
-    boolean mIsSurfaceManagedBySystemOrganizer = false;
-
-    /**
      * When we are in the process of pausing an activity, before starting the
      * next one, this variable holds the activity that is currently being paused.
      *
@@ -453,21 +448,13 @@
 
     void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
             @NonNull String processName) {
-        setTaskFragmentOrganizer(organizer, uid, processName,
-                false /* isSurfaceManagedBySystemOrganizer */);
-    }
-
-    void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
-            @NonNull String processName, boolean isSurfaceManagedBySystemOrganizer) {
         mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
         mTaskFragmentOrganizerUid = uid;
         mTaskFragmentOrganizerProcessName = processName;
-        mIsSurfaceManagedBySystemOrganizer = isSurfaceManagedBySystemOrganizer;
     }
 
     void onTaskFragmentOrganizerRemoved() {
         mTaskFragmentOrganizer = null;
-        mIsSurfaceManagedBySystemOrganizer = false;
     }
 
     /** Whether this TaskFragment is organized by the given {@code organizer}. */
@@ -1548,10 +1535,11 @@
                         next.getTask().mTaskId, next.shortComponentName);
 
                 mAtmService.getAppWarningsLocked().onResumeActivity(next);
-                next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
+                final int topProcessState = mAtmService.mTopProcessState;
+                next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState);
                 next.abortAndClearOptionsAnimation();
                 transaction.setLifecycleStateRequest(
-                        ResumeActivityItem.obtain(next.token, next.app.getReportedProcState(),
+                        ResumeActivityItem.obtain(next.token, topProcessState,
                                 dc.isNextTransitionForward(), next.shouldSendCompatFakeFocus()));
                 mAtmService.getLifecycleManager().scheduleTransaction(transaction);
 
@@ -2454,9 +2442,6 @@
         if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
             return;
         }
-        if (mIsSurfaceManagedBySystemOrganizer) {
-            return;
-        }
         if (mTransitionController.isShellTransitionsEnabled()
                 && !mTransitionController.isCollecting(this)) {
             // TaskFragmentOrganizer doesn't have access to the surface for security reasons, so
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index b23ffa8..c0bf2ce 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -59,6 +59,7 @@
 import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT;
 
+import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
@@ -1272,18 +1273,23 @@
             }
         }
         // Commit wallpaper visibility after activity, because usually the wallpaper target token is
-        // an activity, and wallpaper's visibility is depends on activity's visibility.
+        // an activity, and wallpaper's visibility depends on activity's visibility.
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
             if (wt == null) continue;
             final WindowState target = wt.mDisplayContent.mWallpaperController.getWallpaperTarget();
             final boolean isTargetInvisible = target == null || !target.mToken.isVisible();
-            if (isTargetInvisible || (!wt.isVisibleRequested()
-                    && !mVisibleAtTransitionEndTokens.contains(wt))) {
+            final boolean isWallpaperVisibleAtEnd =
+                    wt.isVisibleRequested() || mVisibleAtTransitionEndTokens.contains(wt);
+            if (isTargetInvisible || !isWallpaperVisibleAtEnd) {
                 ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                         "  Commit wallpaper becoming invisible: %s", wt);
                 wt.commitVisibility(false /* visible */);
             }
+            if (isTargetInvisible) {
+                // Our original target went invisible, so we should look for a new target.
+                wt.mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+            }
         }
         if (committedSomeInvisible) {
             mController.onCommittedInvisibles();
@@ -1408,12 +1414,13 @@
                     false /* forceRelayout */);
         }
         cleanUpInternal();
-        mController.updateAnimatingState();
 
         // Handle back animation if it's already started.
         mController.mAtm.mBackNavigationController.onTransitionFinish(mTargets, this);
         mController.mFinishingTransition = null;
         mController.mSnapshotController.onTransitionFinish(mType, mTargets);
+        // Resume snapshot persist thread after snapshot controller analysis this transition.
+        mController.updateAnimatingState();
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e3d24b..ab3ddbd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -336,6 +336,7 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
 import com.android.server.utils.PriorityDump;
+import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -542,6 +543,12 @@
     @VisibleForTesting
     boolean mSkipActivityRelaunchWhenDocking;
 
+    /** Device default insets types provided non-decor insets. */
+    final int mDecorTypes;
+
+    /** Device default insets types shall be excluded from config app sizes. */
+    final int mConfigTypes;
+
     final boolean mLimitedAlphaCompositing;
     final int mMaxUiWidth;
 
@@ -1185,6 +1192,16 @@
                 com.android.internal.R.bool.config_assistantOnTopOfDream);
         mSkipActivityRelaunchWhenDocking = context.getResources()
                 .getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);
+        final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
+                .getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
+                && Flags.closeToSquareConfigIncludesStatusBar();
+        if (!isScreenSizeDecoupledFromStatusBarAndCutout) {
+            mDecorTypes = WindowInsets.Type.displayCutout() | WindowInsets.Type.navigationBars();
+            mConfigTypes = WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars();
+        } else {
+            mDecorTypes = WindowInsets.Type.navigationBars();
+            mConfigTypes = WindowInsets.Type.navigationBars();
+        }
 
         mLetterboxConfiguration = new LetterboxConfiguration(
                 // Using SysUI context to have access to Material colors extracted from Wallpaper.
@@ -7319,6 +7336,7 @@
         }
     }
 
+    /** This is used when there's no app info available and shall return the system default.*/
     void getStableInsetsLocked(int displayId, Rect outInsets) {
         outInsets.setEmpty();
         final DisplayContent dc = mRoot.getDisplayContent(displayId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 95e2515..89d47bc 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -2122,8 +2122,7 @@
         // actions.
         TaskFragmentOrganizerToken organizerToken = creationParams.getOrganizer();
         taskFragment.setTaskFragmentOrganizer(organizerToken,
-                ownerActivity.getUid(), ownerActivity.info.processName,
-                mTaskFragmentOrganizerController.isSystemOrganizer(organizerToken.asBinder()));
+                ownerActivity.getUid(), ownerActivity.info.processName);
         final int position;
         if (creationParams.getPairedPrimaryFragmentToken() != null) {
             // When there is a paired primary TaskFragment, we want to place the new TaskFragment
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ae2df00..4e17011 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1381,7 +1381,7 @@
             // This window doesn't provide any insets.
             return;
         }
-        if (mGivenInsetsPending) {
+        if (mGivenInsetsPending && mAttrs.providedInsets == null) {
             // The given insets are pending, and they are not reliable for now. The source frame
             // should be updated after the new given insets are sent to window manager.
             return;
@@ -1829,7 +1829,7 @@
         }
         for (int i = mInsetsSourceProviders.size() - 1; i >= 0; i--) {
             final InsetsSource source = mInsetsSourceProviders.valueAt(i).getSource();
-            if ((source.getType() & DisplayPolicy.DecorInsets.CONFIG_TYPES) != 0) {
+            if ((source.getType() & mWmService.mConfigTypes) != 0) {
                 return true;
             }
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 56e385d..0a2e806 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -129,6 +129,7 @@
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.coverage.CoverageService;
 import com.android.server.cpu.CpuMonitorService;
+import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
 import com.android.server.devicestate.DeviceStateManagerService;
 import com.android.server.display.DisplayManagerService;
@@ -964,6 +965,7 @@
             // Only update the timeout after starting all the services so that we use
             // the default timeout to start system server.
             updateWatchdogTimeout(t);
+            CriticalEventLog.getInstance().logSystemServerStarted();
         } catch (Throwable ex) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting system services", ex);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index fe31b9c..a776eec 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -267,7 +267,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DIM);
@@ -305,7 +306,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
@@ -342,7 +344,8 @@
                 /* dozeAfterScreenOff= */ true,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -378,7 +381,8 @@
                 /* dozeAfterScreenOff= */ true,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -414,7 +418,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_OFF);
@@ -451,7 +456,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -486,7 +492,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ false,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -522,7 +529,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ false,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
@@ -557,7 +565,8 @@
                 /* dozeAfterScreenOff= */ false,
                 /* bootCompleted= */ true,
                 /* screenBrightnessBoostInProgress= */ true,
-                /* waitForNegativeProximity= */ false);
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
                 mPowerGroup.mDisplayPowerRequest;
         assertThat(displayPowerRequest.policy).isEqualTo(POLICY_BRIGHT);
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS b/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
deleted file mode 100644
index a0f46e1..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /services/core/java/com/android/server/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING
deleted file mode 100644
index a83d8bf..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timedetector."
-        }
-      ]
-    }
-  ]
-}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/timezone/OWNERS
deleted file mode 100644
index d64cbcd..0000000
--- a/services/tests/servicestests/src/com/android/server/timezone/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 847766
-include /services/core/java/com/android/server/timezone/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING
deleted file mode 100644
index f59188c..0000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
-  "postsubmit": [
-    {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.timezonedetector."
-        }
-      ]
-    }
-  ]
-}
diff --git a/services/tests/timetests/Android.bp b/services/tests/timetests/Android.bp
new file mode 100644
index 0000000..23ab859
--- /dev/null
+++ b/services/tests/timetests/Android.bp
@@ -0,0 +1,27 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "FrameworksTimeServicesTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "device-time-shell-utils",
+        "junit",
+        "junit-params",
+        "mockito-target-minus-junit4",
+        "platform-test-annotations",
+        "services.core",
+        "truth",
+    ],
+    libs: ["android.test.runner"],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+}
diff --git a/services/tests/timetests/AndroidManifest.xml b/services/tests/timetests/AndroidManifest.xml
new file mode 100644
index 0000000..a21d383
--- /dev/null
+++ b/services/tests/timetests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.framework.services.tests.time">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+            android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.framework.services.tests.time"
+            android:label="Frameworks Time Services Tests" />
+
+</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS b/services/tests/timetests/OWNERS
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/OWNERS
rename to services/tests/timetests/OWNERS
diff --git a/services/tests/timetests/TEST_MAPPING b/services/tests/timetests/TEST_MAPPING
new file mode 100644
index 0000000..b24010c
--- /dev/null
+++ b/services/tests/timetests/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows.
+  "postsubmit": [
+    {
+      "name": "FrameworksTimeServicesTests"
+    }
+  ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ArrayMapWithHistoryTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
index 808c1ec..392a48f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/ConfigurationInternalTest.java
@@ -33,14 +33,14 @@
 
 import com.android.server.timedetector.TimeDetectorStrategy.Origin;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.time.Instant;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 /**
  * Tests for {@link ConfigurationInternal} and associated {@link TimeCapabilitiesAndConfig}
  * behavior.
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
rename to services/tests/timetests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
rename to services/tests/timetests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/GnssTimeSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/GnssTimeUpdateServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/NetworkTimeSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/NetworkTimeUpdateServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
rename to services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index dd58135..c64ec72 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -51,6 +51,9 @@
 import com.android.server.timezonedetector.StateChangeListener;
 import com.android.server.timezonedetector.TestStateChangeListener;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -62,9 +65,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 @RunWith(JUnitParamsRunner.class)
 public class TimeDetectorStrategyImplTest {
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 566c6b0..c77fe39 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -35,12 +35,12 @@
 import android.app.time.TimeZoneCapabilitiesAndConfig;
 import android.app.time.TimeZoneConfiguration;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for {@link ConfigurationInternal} and associated {@link TimeZoneCapabilitiesAndConfig}
  * behavior.
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index 1e72369..7439307 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -43,7 +43,7 @@
                 this::notifyChangeListeners);
     }
 
-    public void initializeConfigurationAndStatus(
+    void initializeConfigurationAndStatus(
             ConfigurationInternal configuration, TimeZoneDetectorStatus status) {
         mFakeServiceConfigAccessor.initializeCurrentUserConfiguration(configuration);
         mStatus = Objects.requireNonNull(status);
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/GeolocationTimeZoneSuggestionTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/LocationAlgorithmEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/MetricsTimeZoneDetectorStateTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
index b96c82f..72b1819 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/ShellCommandTestSupport.java
@@ -29,10 +29,17 @@
 public final class ShellCommandTestSupport {
     private ShellCommandTestSupport() {}
 
+    /**
+     * Returns a {@link ShellCommand} from the supplied String, where elements of the command are
+     * separated with spaces. No escaping is performed.
+     */
     public static ShellCommand createShellCommandWithArgsAndOptions(String argsWithSpaces) {
         return createShellCommandWithArgsAndOptions(Arrays.asList(argsWithSpaces.split(" ")));
     }
 
+    /**
+     * Returns a {@link ShellCommand} from the supplied list of command line elements.
+     */
     public static ShellCommand createShellCommandWithArgsAndOptions(List<String> args) {
         ShellCommand command = mock(ShellCommand.class);
         class ArgProvider {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
index f45b3a8..56db9cc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
@@ -28,12 +28,18 @@
     private int mCallingUserId;
     private Integer mCurrentCallingUserId;
 
+    /** Initializes the calling user ID. */
     public void initializeCallingUserId(@UserIdInt int userId) {
         mCallingUserId = userId;
         mCurrentCallingUserId = userId;
     }
 
     @Override
+    public int resolveUserId(int userId, String debugInfo) {
+        return userId;
+    }
+
+    @Override
     public int getCallingUserId() {
         assertNotNull("callingUserId has been cleared", mCurrentCallingUserId);
         return mCurrentCallingUserId;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
similarity index 95%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
index aad06d8..61f9260 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java
@@ -23,6 +23,7 @@
 
     private Integer mCurrentUserId;
 
+    /** Initializes the current user ID. */
     public void initializeCurrentUserId(@UserIdInt int userId) {
         mCurrentUserId = userId;
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestHandler.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestHandler.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestState.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestState.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestState.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
index 9cbf0a3..23f96a2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TestStateChangeListener.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TestStateChangeListener.java
@@ -27,6 +27,7 @@
         mNotificationsReceived++;
     }
 
+    /** Asserts the expected number of notifications have been received, then resets the count. */
     public void assertNotificationsReceivedAndReset(int expectedCount) {
         assertNotificationsReceived(expectedCount);
         resetNotificationsReceivedCount();
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneCanonicalizerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index 03d406f..e52e8b6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -72,6 +72,9 @@
 import com.android.server.SystemTimeZone.TimeZoneConfidence;
 import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
 
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,9 +86,6 @@
 import java.util.List;
 import java.util.function.Function;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
 /**
  * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}.
  */
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
index f8d169b..e05d84f 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/FakeTimeZoneProviderEventPreProcessor.java
@@ -38,6 +38,7 @@
         return timeZoneProviderEvent;
     }
 
+    /** Enters a mode where {@link #preProcess} will always return "uncertain" events. */
     public void enterUncertainMode() {
         mIsUncertain = true;
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/TestSupport.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
rename to services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
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 3ba9400..261b5d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 
 import static junit.framework.TestCase.assertEquals;
@@ -24,9 +25,12 @@
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 
+import android.app.Flags;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
 import android.net.Uri;
+import android.os.Parcel;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
@@ -41,6 +45,7 @@
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
@@ -55,6 +60,28 @@
 @RunWith(AndroidJUnit4.class)
 public class ZenModeConfigTest extends UiServiceTestCase {
 
+    private final String NAME = "name";
+    private final ComponentName OWNER = new ComponentName("pkg", "cls");
+    private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+    private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+    private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+            .authority("authority")
+            .appendPath("path")
+            .appendPath("test")
+            .build();
+
+    private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
+    private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+    private final int TYPE = TYPE_BEDTIME;
+    private final boolean ALLOW_MANUAL = true;
+    private final int ICON_RES_ID = 1234;
+    private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+    private final boolean ENABLED = true;
+    private final int CREATION_TIME = 123;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     @Test
     public void testPriorityOnlyMutingAllNotifications() {
         ZenModeConfig config = getMutedRingerConfig();
@@ -202,7 +229,59 @@
     }
 
     @Test
-    public void testRuleXml() throws Exception {
+    public void testWriteToParcel() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.configurationActivity = CONFIG_ACTIVITY;
+        rule.component = OWNER;
+        rule.conditionId = CONDITION_ID;
+        rule.condition = CONDITION;
+        rule.enabled = ENABLED;
+        rule.creationTime = 123;
+        rule.id = "id";
+        rule.zenMode = INTERRUPTION_FILTER;
+        rule.modified = true;
+        rule.name = NAME;
+        rule.snoozing = true;
+        rule.pkg = OWNER.getPackageName();
+        rule.zenPolicy = POLICY;
+
+        rule.allowManualInvocation = ALLOW_MANUAL;
+        rule.type = TYPE;
+        rule.iconResId = ICON_RES_ID;
+        rule.triggerDescription = TRIGGER_DESC;
+
+        Parcel parcel = Parcel.obtain();
+        rule.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel);
+
+        assertEquals(rule.pkg, parceled.pkg);
+        assertEquals(rule.snoozing, parceled.snoozing);
+        assertEquals(rule.enabler, parceled.enabler);
+        assertEquals(rule.component, parceled.component);
+        assertEquals(rule.configurationActivity, parceled.configurationActivity);
+        assertEquals(rule.condition, parceled.condition);
+        assertEquals(rule.enabled, parceled.enabled);
+        assertEquals(rule.creationTime, parceled.creationTime);
+        assertEquals(rule.modified, parceled.modified);
+        assertEquals(rule.conditionId, parceled.conditionId);
+        assertEquals(rule.name, parceled.name);
+        assertEquals(rule.zenMode, parceled.zenMode);
+
+        assertEquals(rule.allowManualInvocation, parceled.allowManualInvocation);
+        assertEquals(rule.iconResId, parceled.iconResId);
+        assertEquals(rule.type, parceled.type);
+        assertEquals(rule.triggerDescription, parceled.triggerDescription);
+        assertEquals(rule.zenPolicy, parceled.zenPolicy);
+        assertEquals(rule, parceled);
+        assertEquals(rule.hashCode(), parceled.hashCode());
+
+    }
+
+    @Test
+    public void testRuleXml_classic() throws Exception {
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = new ComponentName("a", "a");
         rule.component = new ComponentName("b", "b");
@@ -239,6 +318,58 @@
     }
 
     @Test
+    public void testRuleXml() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.configurationActivity = CONFIG_ACTIVITY;
+        rule.component = OWNER;
+        rule.conditionId = CONDITION_ID;
+        rule.condition = CONDITION;
+        rule.enabled = ENABLED;
+        rule.creationTime = 123;
+        rule.id = "id";
+        rule.zenMode = INTERRUPTION_FILTER;
+        rule.modified = true;
+        rule.name = NAME;
+        rule.snoozing = true;
+        rule.pkg = OWNER.getPackageName();
+        rule.zenPolicy = POLICY;
+        rule.creationTime = CREATION_TIME;
+
+        rule.allowManualInvocation = ALLOW_MANUAL;
+        rule.type = TYPE;
+        rule.iconResId = ICON_RES_ID;
+        rule.triggerDescription = TRIGGER_DESC;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeRuleXml(rule, baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+        assertEquals(rule.pkg, fromXml.pkg);
+        // always resets on reboot
+        assertFalse(fromXml.snoozing);
+        //should all match original
+        assertEquals(rule.component, fromXml.component);
+        assertEquals(rule.configurationActivity, fromXml.configurationActivity);
+        assertNull(fromXml.enabler);
+        assertEquals(rule.condition, fromXml.condition);
+        assertEquals(rule.enabled, fromXml.enabled);
+        assertEquals(rule.creationTime, fromXml.creationTime);
+        assertEquals(rule.modified, fromXml.modified);
+        assertEquals(rule.conditionId, fromXml.conditionId);
+        assertEquals(rule.name, fromXml.name);
+        assertEquals(rule.zenMode, fromXml.zenMode);
+        assertEquals(rule.creationTime, fromXml.creationTime);
+
+        assertEquals(rule.allowManualInvocation, fromXml.allowManualInvocation);
+        assertEquals(rule.type, fromXml.type);
+        assertEquals(rule.triggerDescription, fromXml.triggerDescription);
+        assertEquals(rule.iconResId, fromXml.iconResId);
+    }
+
+    @Test
     public void testRuleXml_pkg_component() throws Exception {
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = new ComponentName("a", "a");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index bcd807a..fd3d5e9b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -23,6 +23,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import android.app.AutomaticZenRule;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.provider.Settings;
@@ -229,6 +230,10 @@
         rule.name = "name";
         rule.snoozing = true;
         rule.pkg = "a";
+        rule.allowManualInvocation = true;
+        rule.type = AutomaticZenRule.TYPE_SCHEDULE_TIME;
+        rule.iconResId = 123;
+        rule.triggerDescription = "At night";
         return rule;
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index e22c104..0349ad9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
@@ -71,6 +72,7 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
+import android.app.Flags;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
 import android.content.ComponentName;
@@ -88,6 +90,7 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.Condition;
@@ -120,6 +123,7 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -150,6 +154,28 @@
     private static final int CUSTOM_PKG_UID = 1;
     private static final String CUSTOM_RULE_ID = "custom_rule";
 
+    private final String NAME = "name";
+    private final ComponentName OWNER = new ComponentName("pkg", "cls");
+    private final ComponentName CONFIG_ACTIVITY = new ComponentName("pkg", "act");
+    private final ZenPolicy POLICY = new ZenPolicy.Builder().allowAlarms(true).build();
+    private final Uri CONDITION_ID = new Uri.Builder().scheme("scheme")
+            .authority("authority")
+            .appendPath("path")
+            .appendPath("test")
+            .build();
+
+    private final Condition CONDITION = new Condition(CONDITION_ID, "", Condition.STATE_TRUE);
+    private final String TRIGGER_DESC = "Every Night, 10pm to 6am";
+    private final int TYPE = TYPE_BEDTIME;
+    private final boolean ALLOW_MANUAL = true;
+    private final int ICON_RES_ID = 1234;
+    private final int INTERRUPTION_FILTER = Settings.Global.ZEN_MODE_ALARMS;
+    private final boolean ENABLED = true;
+    private final int CREATION_TIME = 123;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     ConditionProviders mConditionProviders;
     @Mock NotificationManager mNotificationManager;
     @Mock PackageManager mPackageManager;
@@ -1961,6 +1987,26 @@
 
     @Test
     public void testSetManualZenMode() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        setupZenConfig();
+
+        // note that caller=null because that's how it comes in from NMS.setZenMode
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null, "",
+                Process.SYSTEM_UID, true);
+
+        // confirm that setting zen mode via setManualZenMode changed the zen mode correctly
+        assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, mZenModeHelper.mZenMode);
+        assertEquals(true, mZenModeHelper.mConfig.manualRule.allowManualInvocation);
+
+        // and also that it works to turn it back off again
+        mZenModeHelper.setManualZenMode(Global.ZEN_MODE_OFF, null, null, "",
+                Process.SYSTEM_UID, true);
+
+        assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
+    }
+
+    @Test
+    public void testSetManualZenMode_legacy() {
         setupZenConfig();
 
         // note that caller=null because that's how it comes in from NMS.setZenMode
@@ -2607,6 +2653,47 @@
         assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking());  // custom stricter
     }
 
+    @Test
+    public void testCreateAutomaticZenRule_allFields() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.configurationActivity = CONFIG_ACTIVITY;
+        rule.component = OWNER;
+        rule.conditionId = CONDITION_ID;
+        rule.condition = CONDITION;
+        rule.enabled = ENABLED;
+        rule.creationTime = 123;
+        rule.id = "id";
+        rule.zenMode = INTERRUPTION_FILTER;
+        rule.modified = true;
+        rule.name = NAME;
+        rule.snoozing = true;
+        rule.pkg = OWNER.getPackageName();
+        rule.zenPolicy = POLICY;
+
+        rule.allowManualInvocation = ALLOW_MANUAL;
+        rule.type = TYPE;
+        rule.iconResId = ICON_RES_ID;
+        rule.triggerDescription = TRIGGER_DESC;
+
+        AutomaticZenRule actual = mZenModeHelper.createAutomaticZenRule(rule);
+
+        assertEquals(NAME, actual.getName());
+        assertEquals(OWNER, actual.getOwner());
+        assertEquals(CONDITION_ID, actual.getConditionId());
+        assertEquals(NotificationManager.INTERRUPTION_FILTER_ALARMS,
+                actual.getInterruptionFilter());
+        assertEquals(ENABLED, actual.isEnabled());
+        assertEquals(POLICY, actual.getZenPolicy());
+        assertEquals(CONFIG_ACTIVITY, actual.getConfigurationActivity());
+        assertEquals(TYPE, actual.getType());
+        assertEquals(ALLOW_MANUAL, actual.isManualInvocationAllowed());
+        assertEquals(CREATION_TIME, actual.getCreationTime());
+        assertEquals(OWNER.getPackageName(), actual.getPackageName());
+        assertEquals(ICON_RES_ID, actual.getIconResId());
+        assertEquals(TRIGGER_DESC, actual.getTriggerDescription());
+    }
+
     private void setupZenConfig() {
         mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
         mZenModeHelper.mConfig.allowAlarms = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index bf86563..c782d3e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -384,33 +384,36 @@
         assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
         assertFalse(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
 
-        navbar.removeIfPossible();
-        assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
-                di.logicalHeight).mNonDecorInsets.bottom);
-
         final WindowState statusBar = createStatusBarWithProvidedInsets(mDisplayContent);
-        assertTrue(statusBar.providesDisplayDecorInsets()
-                && displayPolicy.updateDecorInsetsInfo());
-        assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
-                di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+        if (mWm.mConfigTypes == WindowInsets.Type.navigationBars()) {
+            assertFalse(statusBar.providesDisplayDecorInsets()
+                    && displayPolicy.updateDecorInsetsInfo());
+            assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation,
+                    di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+        } else {
+            assertTrue(statusBar.providesDisplayDecorInsets()
+                    && displayPolicy.updateDecorInsetsInfo());
+            assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
+                    di.logicalWidth, di.logicalHeight).mConfigInsets.top);
+        }
 
         // Add a window that provides the same insets in current rotation. But it specifies
         // different insets in other rotations.
-        final WindowState bar2 = createWindow(null, statusBar.mAttrs.type, "bar2");
+        final WindowState bar2 = createWindow(null, navbar.mAttrs.type, "bar2");
         bar2.mAttrs.providedInsets = new InsetsFrameProvider[] {
-                new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
-                        .setInsetsSize(Insets.of(0, STATUS_BAR_HEIGHT, 0, 0))
+                new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
+                        .setInsetsSize(Insets.of(0, 0, 0, NAV_BAR_HEIGHT))
         };
         bar2.mAttrs.setFitInsetsTypes(0);
         bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
-        final int doubleHeightFor90 = STATUS_BAR_HEIGHT * 2;
+        final int doubleHeightFor90 = NAV_BAR_HEIGHT * 2;
         for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) {
             final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
             params.setFitInsetsTypes(0);
             if (i == Surface.ROTATION_90) {
                 params.providedInsets = new InsetsFrameProvider[] {
-                        new InsetsFrameProvider(bar2, 0, WindowInsets.Type.statusBars())
-                                .setInsetsSize(Insets.of(0, doubleHeightFor90, 0, 0))
+                        new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
+                                .setInsetsSize(Insets.of(0, 0, 0, doubleHeightFor90))
                 };
             } else {
                 params.providedInsets = bar2.mAttrs.providedInsets;
@@ -422,7 +425,12 @@
         assertFalse(displayPolicy.updateDecorInsetsInfo());
         // The insets in other rotations should be still updated.
         assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90,
-                di.logicalHeight, di.logicalWidth).mConfigInsets.top);
+                di.logicalHeight, di.logicalWidth).mConfigInsets.bottom);
+
+        navbar.removeIfPossible();
+        bar2.removeIfPossible();
+        assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
+                di.logicalHeight).mNonDecorInsets.bottom);
     }
 
     @SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index c4d03be..49a8886 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.window.flags.Flags.explicitRefreshRateHints;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -282,6 +283,9 @@
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
 
+        if (explicitRefreshRateHints()) {
+            return;
+        }
         overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
                 overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
                 false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -320,6 +324,9 @@
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE);
 
+        if (explicitRefreshRateHints()) {
+            return;
+        }
         overrideWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
                 overrideWindow.getPendingTransaction(), mock(AnimationAdapter.class),
                 false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
@@ -342,6 +349,9 @@
         assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE);
         assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE);
 
+        if (explicitRefreshRateHints()) {
+            return;
+        }
         window.mActivityRecord.mSurfaceAnimator.startAnimation(
                 window.getPendingTransaction(), mock(AnimationAdapter.class),
                 false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 03188f8..9af5ba5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -376,6 +376,9 @@
 
         mWmService.onInitReady();
         mAtmService.setWindowManager(mWmService);
+        // Avoid real display events interfering with the test. Also avoid leaking registration.
+        mContext.getSystemService(DisplayManager.class)
+                .unregisterDisplayListener(mAtmService.mRootWindowContainer);
         mWmService.mDisplayEnabled = true;
         mWmService.mDisplayReady = true;
         mAtmService.getTransitionController().mIsWaitingForDisplayEnabled = false;
@@ -413,12 +416,6 @@
             }
         }
 
-        if (mAtmService != null) {
-            // Unregister display listener from root to avoid issues with subsequent tests.
-            mContext.getSystemService(DisplayManager.class)
-                    .unregisterDisplayListener(mAtmService.mRootWindowContainer);
-        }
-
         for (int i = mDeviceConfigListeners.size() - 1; i >= 0; i--) {
             DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListeners.get(i));
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 7822071..ec068be 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -27,12 +27,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -130,17 +126,6 @@
     }
 
     @Test
-    public void testUpdateOrganizedTaskFragmentSurface_noSurfaceUpdateWhenOrganizedBySystem() {
-        clearInvocations(mTransaction);
-        mTaskFragment.mIsSurfaceManagedBySystemOrganizer = true;
-
-        mTaskFragment.updateOrganizedTaskFragmentSurface();
-
-        verify(mTransaction, never()).setPosition(eq(mLeash), anyFloat(), anyFloat());
-        verify(mTransaction, never()).setWindowCrop(eq(mLeash), anyInt(), anyInt());
-    }
-
-    @Test
     public void testShouldStartChangeTransition_relativePositionChange() {
         final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
                 ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index 8fecbb9..4b54e44 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -132,6 +132,10 @@
         mPersister.persistSnapshot(2, mTestUserId, createSnapshot());
         mPersister.persistSnapshot(3, mTestUserId, createSnapshot());
         mPersister.persistSnapshot(4, mTestUserId, createSnapshot());
+        // Verify there should only keep the latest request when received a duplicated id.
+        mPersister.persistSnapshot(4, mTestUserId, createSnapshot());
+        // Expected 3: One remove obsolete request, two persist request.
+        assertEquals(3, mSnapshotPersistQueue.peekQueueSize());
         mSnapshotPersistQueue.setPaused(false);
         mSnapshotPersistQueue.waitForQueueEmpty();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2689e9d..a83caa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2454,6 +2454,7 @@
             spyOn(perfHinter);
             doAnswer(invocation -> {
                 session[0] = (SystemPerformanceHinter.HighPerfSession) invocation.callRealMethod();
+                spyOn(session[0]);
                 return session[0];
             }).when(perfHinter).createSession(anyInt(), anyInt(), anyString());
         }
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 11cbcb1..4b1a726 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -568,6 +568,7 @@
     private final int mSkip464Xlat;
     private final boolean mAlwaysOn;
     private final @InfrastructureBitmask int mInfrastructureBitmask;
+    private final boolean mEsimBootstrapProvisioning;
 
     /**
      * Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
@@ -979,6 +980,18 @@
         return mInfrastructureBitmask;
     }
 
+    /**
+     * Returns esim bootstrap provisioning flag for which the APN can be used on. For example,
+     * some APNs are only allowed to bring up network, when the device esim bootstrap provisioning
+     * is being activated.
+     *
+     * {@code true} if the APN is used for eSIM bootstrap provisioning, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isEsimBootstrapProvisioning() {
+        return mEsimBootstrapProvisioning;
+    }
+
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
@@ -1016,6 +1029,7 @@
         this.mSkip464Xlat = builder.mSkip464Xlat;
         this.mAlwaysOn = builder.mAlwaysOn;
         this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
+        this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning;
     }
 
     /**
@@ -1097,6 +1111,8 @@
                 .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)
                 .setInfrastructureBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
+                .setEsimBootstrapProvisioning(cursor.getInt(
+                        cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1)
                 .buildWithoutCheck();
     }
 
@@ -1137,6 +1153,7 @@
                 .setSkip464Xlat(apn.mSkip464Xlat)
                 .setAlwaysOn(apn.mAlwaysOn)
                 .setInfrastructureBitmask(apn.mInfrastructureBitmask)
+                .setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning)
                 .buildWithoutCheck();
     }
 
@@ -1184,6 +1201,7 @@
         sb.append(", ").append(mAlwaysOn);
         sb.append(", ").append(mInfrastructureBitmask);
         sb.append(", ").append(Objects.hash(mUser, mPassword));
+        sb.append(", ").append(mEsimBootstrapProvisioning);
         return sb.toString();
     }
 
@@ -1247,7 +1265,7 @@
                 mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask,
                 mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime,
                 mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat,
-                mAlwaysOn, mInfrastructureBitmask);
+                mAlwaysOn, mInfrastructureBitmask, mEsimBootstrapProvisioning);
     }
 
     @Override
@@ -1289,7 +1307,8 @@
                 && mCarrierId == other.mCarrierId
                 && mSkip464Xlat == other.mSkip464Xlat
                 && mAlwaysOn == other.mAlwaysOn
-                && mInfrastructureBitmask == other.mInfrastructureBitmask;
+                && mInfrastructureBitmask == other.mInfrastructureBitmask
+                && mEsimBootstrapProvisioning == other.mEsimBootstrapProvisioning;
     }
 
     /**
@@ -1340,7 +1359,8 @@
                 && Objects.equals(mCarrierId, other.mCarrierId)
                 && Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
                 && Objects.equals(mAlwaysOn, other.mAlwaysOn)
-                && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask);
+                && Objects.equals(mInfrastructureBitmask, other.mInfrastructureBitmask)
+                && Objects.equals(mEsimBootstrapProvisioning, other.mEsimBootstrapProvisioning);
     }
 
     /**
@@ -1378,7 +1398,9 @@
                 && Objects.equals(this.mCarrierId, other.mCarrierId)
                 && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
                 && Objects.equals(this.mAlwaysOn, other.mAlwaysOn)
-                && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask);
+                && Objects.equals(this.mInfrastructureBitmask, other.mInfrastructureBitmask)
+                && Objects.equals(this.mEsimBootstrapProvisioning,
+                other.mEsimBootstrapProvisioning);
     }
 
     // Equal or one is null.
@@ -1451,6 +1473,7 @@
         apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
         apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
         apnValue.put(Telephony.Carriers.INFRASTRUCTURE_BITMASK, mInfrastructureBitmask);
+        apnValue.put(Telephony.Carriers.ESIM_BOOTSTRAP_PROVISIONING, mEsimBootstrapProvisioning);
         return apnValue;
     }
 
@@ -1724,6 +1747,7 @@
         dest.writeInt(mSkip464Xlat);
         dest.writeBoolean(mAlwaysOn);
         dest.writeInt(mInfrastructureBitmask);
+        dest.writeBoolean(mEsimBootstrapProvisioning);
     }
 
     private static ApnSetting readFromParcel(Parcel in) {
@@ -1760,6 +1784,7 @@
                 .setSkip464Xlat(in.readInt())
                 .setAlwaysOn(in.readBoolean())
                 .setInfrastructureBitmask(in.readInt())
+                .setEsimBootstrapProvisioning(in.readBoolean())
                 .buildWithoutCheck();
     }
 
@@ -1842,6 +1867,7 @@
         private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
         private boolean mAlwaysOn;
         private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR;
+        private boolean mEsimBootstrapProvisioning;
 
         /**
          * Default constructor for Builder.
@@ -2280,6 +2306,19 @@
         }
 
         /**
+         * Sets esim bootstrap provisioning flag
+         *
+         * @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap
+         * provisioning, {@code false} otherwise.
+         * @hide
+         */
+        @NonNull
+        public Builder setEsimBootstrapProvisioning(boolean esimBootstrapProvisioning) {
+            this.mEsimBootstrapProvisioning = esimBootstrapProvisioning;
+            return this;
+        }
+
+        /**
          * Builds {@link ApnSetting} from this builder.
          *
          * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index 790da34..12a57d5 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.Presubmit
 import android.tools.common.datatypes.Rect
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -149,8 +150,12 @@
                 ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
             }
             pipLayerList.zipWithNext { previous, current ->
-                // TODO(b/290987990): Add checks for visibleRegion.
-                current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+                if (startDisplayBounds.width > startDisplayBounds.height) {
+                    // Only verify when the display is landscape, because otherwise the final pip
+                    // window can be to the left of the original secondary activity.
+                    current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+                }
+                current.screenBounds.overlaps(previous.screenBounds.region)
                 current.screenBounds.notBiggerThan(previous.screenBounds.region)
             }
         }
@@ -161,6 +166,61 @@
         }
     }
 
+    /** The secondary layer should never jump to the left. */
+    @Presubmit
+    @Test
+    fun secondaryLayerNotJumpToLeft() {
+        flicker.assertLayers {
+            invoke("secondaryLayerNotJumpToLeft") {
+                val secondaryVisibleRegion =
+                    it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+                if (secondaryVisibleRegion.region.isNotEmpty) {
+                    check { "left" }
+                        .that(secondaryVisibleRegion.region.bounds.left)
+                        .isGreater(0)
+                }
+            }
+        }
+    }
+
+    /**
+     * The pip overlay layer should cover exactly the secondary activity layer when both are
+     * visible.
+     */
+    @Presubmit
+    @Test
+    fun pipContentOverlayLayerCoversExactlySecondaryLayer() {
+        flicker.assertLayers {
+            isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+                .then()
+                .isVisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+                .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+                .invoke("pipContentOverlayLayerCoversExactlySecondaryLayer") {
+                    val overlayVisibleRegion =
+                        it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+                    val secondaryVisibleRegion =
+                        it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+                    overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region)
+                }
+                .then()
+                .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+                .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // Expected for the main activity to become invisible for 1-2 frames because the snapshot
+        // covers it.
+        flicker.assertLayers {
+            visibleLayersShownMoreThanOneConsecutiveEntry(
+                LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+                        listOf(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+            )
+        }
+    }
+
     companion object {
         /** {@inheritDoc} */
         private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
new file mode 100644
index 0000000..afaa3f0
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -0,0 +1,36 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "ConcurrentMultiSessionImeTest",
+    srcs: ["src/**/*.java"],
+    libs: ["android.test.runner"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "platform-test-annotations",
+        "platform-test-rules",
+        "truth",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+    sdk_version: "current",
+
+    // Store test artifacts in separated directories for easier debugging.
+    per_testcase_directory: true,
+}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
new file mode 100644
index 0000000..0defe5b
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright (C) 2023 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.inputmethod.multisessiontest">
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation>
+</manifest>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
new file mode 100644
index 0000000..fd598c5
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration description="Config for Concurrent Multi-Session IME tests">
+    <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+        type="module_controller">
+        <option name="required-feature" value="android.software.input_methods" />
+
+        <!-- Currently enabled to automotive only -->
+        <option name="required-feature" value="android.hardware.type.automotive" />
+    </object>
+    <option name="test-suite-tag" value="apct" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+        <option name="teardown-command"
+            value="settings delete secure show_ime_with_hard_keyboard" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="force-install-mode" value="FULL" />
+        <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.inputmethod.multisessiontest" />
+    </test>
+</configuration>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
new file mode 100644
index 0000000..b66ceba
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod.multisessiontest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ConcurrentMultiUserTest {
+
+    @Before
+    public void doBeforeEachTest() {
+        // No op
+    }
+
+    @Test
+    public void behaviorBeingTested_expectedResult() {
+        // Sample test
+        Context context =
+                InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertThat(context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE)).isTrue();
+        assertThat(context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_INPUT_METHODS)).isTrue();
+    }
+}
diff --git a/tests/inputmethod/OWNERS b/tests/inputmethod/OWNERS
new file mode 100644
index 0000000..6bb4b17
--- /dev/null
+++ b/tests/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index d749f07..12c7841 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -20,6 +20,7 @@
 import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
@@ -197,9 +198,9 @@
         if (b == null) {
             nativeWriteInt(nativePtr, -1);
         } else {
-            final var alignedSize = align4(b.length);
+            final var alignedSize = align4(len);
 
-            nativeWriteInt(nativePtr, b.length);
+            nativeWriteInt(nativePtr, len);
 
             p.ensureMoreCapacity(alignedSize);
 
@@ -280,6 +281,7 @@
                     + data.length + " given=" + destLen);
             return false;
         }
+        System.arraycopy(data, 0, dest, 0, data.length);
         return true;
     }
 
@@ -289,7 +291,12 @@
             return null;
         }
         var p = getInstance(nativePtr);
-        p.ensureDataAvailable(size);
+        try {
+            p.ensureDataAvailable(align4(size));
+        } catch (Exception e) {
+            System.err.println(e.toString());
+            return null;
+        }
 
         var bytes = new byte[size];
         System.arraycopy(p.mBuffer, p.mPos, bytes, 0, size);
@@ -301,7 +308,10 @@
     public static int nativeReadInt(long nativePtr) {
         var p = getInstance(nativePtr);
 
-        p.ensureDataAvailable(Integer.BYTES);
+        if (p.mSize - p.mPos < 4) {
+            // Match native impl that returns "0" when not enough data
+            return 0;
+        }
 
         var ret = (((p.mBuffer[p.mPos++] & 0xff) << 24)
                 | ((p.mBuffer[p.mPos++] & 0xff) << 16)
@@ -341,11 +351,16 @@
     }
 
     public static byte[] nativeMarshall(long nativePtr) {
-        throw new RuntimeException("Not implemented yet");
+        var p = getInstance(nativePtr);
+        return Arrays.copyOf(p.mBuffer, p.mSize);
     }
     public static void nativeUnmarshall(
             long nativePtr, byte[] data, int offset, int length) {
-        throw new RuntimeException("Not implemented yet");
+        var p = getInstance(nativePtr);
+        p.ensureMoreCapacity(length);
+        System.arraycopy(data, offset, p.mBuffer, p.mPos, length);
+        p.mPos += length;
+        p.updateSize();
     }
     public static int nativeCompareData(long thisNativePtr, long otherNativePtr) {
         throw new RuntimeException("Not implemented yet");
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index a51bdcf..1bcf364 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -22,6 +22,8 @@
 import org.objectweb.asm.Type
 import org.objectweb.asm.tree.AnnotationNode
 import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldNode
+import org.objectweb.asm.tree.MethodNode
 
 
 /** Name of the class initializer method. */
@@ -175,3 +177,93 @@
         else -> false
     }
 }
+
+fun ClassNode.isEnum(): Boolean {
+    return (this.access and Opcodes.ACC_ENUM) != 0
+}
+
+fun ClassNode.isAnnotation(): Boolean {
+    return (this.access and Opcodes.ACC_ANNOTATION) != 0
+}
+
+fun ClassNode.isSynthetic(): Boolean {
+    return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+fun MethodNode.isSynthetic(): Boolean {
+    return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+fun FieldNode.isEnum(): Boolean {
+    return (this.access and Opcodes.ACC_ENUM) != 0
+}
+
+fun FieldNode.isSynthetic(): Boolean {
+    return (this.access and Opcodes.ACC_SYNTHETIC) != 0
+}
+
+/*
+
+Dump of the members of TinyFrameworkEnumSimple:
+
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple	keep
+  field Cat	keep (ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM)
+  field Dog	keep
+  field $VALUES	keep (ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC)
+
+  method values	()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;	keep
+    ^- NOT synthetic (ACC_PUBLIC, ACC_STATIC)
+  method valueOf	(Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;	keep
+    ^- NOT synthetic (ACC_PUBLIC, ACC_STATIC)
+  method <init>	(Ljava/lang/String;I)V	keep
+    ^- NOT synthetic (ACC_PRIVATE)
+
+  method $values	()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;	keep
+     (ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC)
+  method <clinit>	()V	keep
+
+Dump of the members of TinyFrameworkEnumSimple:
+
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex	keep
+  field RED	keep
+  field BLUE	keep
+  field GREEN	keep
+  field mLongName	keep
+  field mShortName	keep
+  field $VALUES	keep
+  method values	()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;	keep
+  method valueOf	(Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;	keep
+  method <init>	(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V	keep
+  method getLongName	()Ljava/lang/String;	keep
+  method getShortName	()Ljava/lang/String;	keep
+  method $values	()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;	keep
+  method <clinit>	()V	keep
+
+ */
+
+fun isAutoGeneratedEnumMember(mn: MethodNode): Boolean {
+    if (mn.isSynthetic()) {
+        return true
+    }
+    if (mn.name == "<init>" && mn.desc == "(Ljava/lang/String;I)V") {
+        return true
+    }
+    if (mn.name == "<clinit>" && mn.desc == "()V") {
+        return true
+    }
+    if (mn.name == "values" && mn.desc.startsWith("()")) {
+        return true
+    }
+    if (mn.name == "valueOf" && mn.desc.startsWith("(Ljava/lang/String;)")) {
+        return true
+    }
+
+    return false
+}
+
+fun isAutoGeneratedEnumMember(fn: FieldNode): Boolean {
+    if (fn.isSynthetic() || fn.isEnum()) {
+        return true
+    }
+    return false
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 07a023c..9c0fa69 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -22,6 +22,9 @@
 import com.android.hoststubgen.asm.isAnonymousInnerClass
 import com.android.hoststubgen.log
 import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isAnnotation
+import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
+import com.android.hoststubgen.asm.isEnum
 import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
 import org.objectweb.asm.tree.ClassNode
 
@@ -57,18 +60,8 @@
     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
         val fallback = super.getPolicyForClass(className)
 
-        // TODO: This check should be cached.
         val cn = classes.getClass(className)
 
-        if (cn.superName == "java/lang/Enum" &&
-                fallback.policy == FilterPolicy.Keep) {
-            return FilterPolicy.KeepClass.withReason("enum")
-        }
-        if (cn.interfaces.contains("java/lang/annotation/Annotation") &&
-                fallback.policy == FilterPolicy.Keep) {
-            return FilterPolicy.KeepClass.withReason("annotation")
-        }
-
         // Use the implicit policy, if any.
         getClassImplicitPolicy(className, cn)?.let { return it }
 
@@ -95,16 +88,78 @@
             }
         }
 
+        val cn = classes.getClass(className)
+
         // If we throw from the static initializer, the class would be useless, so we convert it
         // "keep" instead.
-        if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC &&
-                fallback.policy == FilterPolicy.Throw) {
+        // Unless it's an enum -- in that case, the below code would handle it.
+        if (!cn.isEnum() &&
+                fallback.policy == FilterPolicy.Throw &&
+                methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
             // TODO Maybe show a warning?? But that'd be too noisy with --default-throw.
             return FilterPolicy.Ignore.withReason(
                 "'throw' on static initializer is handled as 'ignore'" +
                         " [original throw reason: ${fallback.reason}]")
         }
 
+        val classPolicy = super.getPolicyForClass(className)
+
+        log.d("Class ${cn.name} Class policy: $classPolicy")
+        if (classPolicy.policy.needsInImpl) {
+            // Do it only when the class needs to be kept...
+
+            // Member policy should be "keep" or "stub".
+            val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+
+            // Keep (or stub) the generated enum members.
+            if (cn.isEnum()) {
+                classes.findMethod(className, methodName, descriptor)?.let { mn ->
+                    if (isAutoGeneratedEnumMember(mn)) {
+                        return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
+                    }
+                }
+            }
+
+            // Keep (or stub) all members of annotations.
+            if (cn.isAnnotation()) {
+                return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
+            }
+        }
+
+        return fallback
+    }
+
+    override fun getPolicyForField(
+            className: String,
+            fieldName: String
+    ): FilterPolicyWithReason {
+        val fallback = super.getPolicyForField(className, fieldName)
+
+        val cn = classes.getClass(className)
+        val classPolicy = super.getPolicyForClass(className)
+
+        log.d("Class ${cn.name} Class policy: $classPolicy")
+        if (classPolicy.policy.needsInImpl) {
+            // Do it only when the class needs to be kept...
+
+            // Member policy should be "keep" or "stub".
+            val memberPolicy = classPolicy.policy.resolveClassWidePolicy()
+
+            // Keep (or stub) the generated enum members.
+            if (cn.isEnum()) {
+                classes.findField(className, fieldName)?.let { fn ->
+                    if (isAutoGeneratedEnumMember(fn)) {
+                        return memberPolicy.withReason(classPolicy.reason).wrapReason("enum")
+                    }
+                }
+            }
+
+            // Keep (or stub) all members of annotations.
+            if (cn.isAnnotation()) {
+                return memberPolicy.withReason(classPolicy.reason).wrapReason("annotation")
+            }
+        }
+
         return fallback
     }
 }
\ No newline at end of file
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 f627c6e..78a4fa6 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
@@ -879,6 +879,314 @@
     android.hosttest.annotation.HostSideTestStub
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                        // java/lang/Enum
+  interfaces: 0, fields: 6, methods: 7, attributes: 3
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private final java.lang.String mLongName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mShortName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+         x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=5, args_size=5
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: aload_0
+         x: aload_3
+         x: putfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: aload_0
+        x: aload         4
+        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+            0      18     3 longName   Ljava/lang/String;
+            0      18     4 shortName   Ljava/lang/String;
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mLongName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mShortName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_3
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                  // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                  // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_2
+        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=6, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: ldc           #x                 // String RED
+         x: iconst_0
+         x: ldc           #x                 // String Red
+         x: ldc           #x                 // String R
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                  // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String GREEN
+        x: iconst_1
+        x: ldc           #x                 // String Green
+        x: ldc           #x                 // String G
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                  // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String BLUE
+        x: iconst_2
+        x: ldc           #x                 // String Blue
+        x: ldc           #x                 // String B
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                          // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                        // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 3
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+         x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=3, args_size=3
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    Signature: #x                          // ()V
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_2
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                  // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                  // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: ldc           #x                 // String CAT
+         x: iconst_0
+         x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                  // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String DOG
+        x: iconst_1
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                  // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                          // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
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 d7f0149..df63815 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
@@ -446,6 +446,230 @@
     android.hosttest.annotation.HostSideTestStub
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 4, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=5, args_size=5
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=3, args_size=3
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    Signature: #x                          // ()V
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
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 131e0b1..2218d8d 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
@@ -798,6 +798,324 @@
     android.hosttest.annotation.HostSideTestStub
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 6, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private final java.lang.String mLongName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mShortName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+         x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=5, args_size=5
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: aload_0
+         x: aload_3
+         x: putfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: aload_0
+        x: aload         4
+        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+            0      18     3 longName   Ljava/lang/String;
+            0      18     4 shortName   Ljava/lang/String;
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mLongName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mShortName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_3
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_2
+        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=6, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: ldc           #x                 // String RED
+         x: iconst_0
+         x: ldc           #x                 // String Red
+         x: ldc           #x                 // String R
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String GREEN
+        x: iconst_1
+        x: ldc           #x                 // String Green
+        x: ldc           #x                 // String G
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String BLUE
+        x: iconst_2
+        x: ldc           #x                 // String Blue
+        x: ldc           #x                 // String B
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+         x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=3, args_size=3
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    Signature: #x                          // ()V
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_2
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: ldc           #x                 // String CAT
+         x: iconst_0
+         x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String DOG
+        x: iconst_1
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
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 d7f0149..df63815 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
@@ -446,6 +446,230 @@
     android.hosttest.annotation.HostSideTestStub
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 4, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=5, args_size=5
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=3, args_size=3
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+    Signature: #x                          // ()V
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Stub!
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
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 3318c7d..3ac9c6a 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
@@ -1046,6 +1046,390 @@
     android.hosttest.annotation.HostSideTestStub
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 6, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private final java.lang.String mLongName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mShortName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+        x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String valueOf
+         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: aload_0
+        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=4, locals=5, args_size=5
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: iload_2
+        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+        x: aload_0
+        x: aload_3
+        x: putfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: aload_0
+        x: aload         4
+        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+           11      18     3 longName   Ljava/lang/String;
+           11      18     4 shortName   Ljava/lang/String;
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String getLongName
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String getShortName
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String $values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_3
+        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: iconst_0
+        x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_2
+        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=6, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        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: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String RED
+        x: iconst_0
+        x: ldc           #x                 // String Red
+        x: ldc           #x                 // String R
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String GREEN
+        x: iconst_1
+        x: ldc           #x                 // String Green
+        x: ldc           #x                 // String G
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String BLUE
+        x: iconst_2
+        x: ldc           #x                // String Blue
+        x: ldc           #x                // String B
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokestatic  #x                // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestStub
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+        x: areturn
+      LineNumberTable:
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String valueOf
+         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: aload_0
+        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  name   Ljava/lang/String;
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=4, locals=3, args_size=3
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Ljava/lang/String;I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: iload_2
+        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    Signature: #x                          // ()V
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String $values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_2
+        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: iconst_0
+        x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        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: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String CAT
+        x: iconst_0
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String DOG
+        x: iconst_1
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: return
+      LineNumberTable:
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedStubClass
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedKeepClass
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
new file mode 100644
index 0000000..51f4818
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
@@ -0,0 +1,51 @@
+/*
+ * 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.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestKeep;
+import android.hosttest.annotation.HostSideTestStub;
+
+@HostSideTestStub
+public enum TinyFrameworkEnumComplex {
+    @HostSideTestStub
+    RED("Red", "R"),
+    @HostSideTestStub
+    GREEN("Green", "G"),
+    @HostSideTestStub
+    BLUE("Blue", "B");
+
+    @HostSideTestKeep
+    private final String mLongName;
+
+    @HostSideTestKeep
+    private final String mShortName;
+
+    @HostSideTestStub
+    TinyFrameworkEnumComplex(String longName, String shortName) {
+        mLongName = longName;
+        mShortName = shortName;
+    }
+
+    @HostSideTestStub
+    public String getLongName() {
+        return mLongName;
+    }
+
+    @HostSideTestStub
+    public String getShortName() {
+        return mShortName;
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
new file mode 100644
index 0000000..f440d86
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestStub;
+
+@HostSideTestStub
+public enum TinyFrameworkEnumSimple {
+    @HostSideTestStub
+    CAT,
+    @HostSideTestStub
+    DOG,
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 29aabc7..ecb181b 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -199,4 +199,43 @@
     public void testPackageRedirect() throws Exception {
         assertThat(TinyFrameworkPackageRedirect.foo(1)).isEqualTo(1);
     }
+
+    @Test
+    public void testEnumSimple() throws Exception {
+        assertThat(TinyFrameworkEnumSimple.CAT.ordinal()).isEqualTo(0);
+        assertThat(TinyFrameworkEnumSimple.CAT.name()).isEqualTo("CAT");
+
+        assertThat(TinyFrameworkEnumSimple.DOG.ordinal()).isEqualTo(1);
+        assertThat(TinyFrameworkEnumSimple.DOG.name()).isEqualTo("DOG");
+
+        assertThat(TinyFrameworkEnumSimple.valueOf("DOG").ordinal()).isEqualTo(1);
+
+        assertThat(TinyFrameworkEnumSimple.values()).isEqualTo(
+                new TinyFrameworkEnumSimple[] {
+                        TinyFrameworkEnumSimple.CAT,
+                        TinyFrameworkEnumSimple.DOG,
+                }
+        );
+    }
+
+    @Test
+    public void testEnumComplex() throws Exception {
+        assertThat(TinyFrameworkEnumComplex.RED.ordinal()).isEqualTo(0);
+        assertThat(TinyFrameworkEnumComplex.RED.name()).isEqualTo("RED");
+
+        assertThat(TinyFrameworkEnumComplex.RED.getShortName()).isEqualTo("R");
+
+        assertThat(TinyFrameworkEnumComplex.GREEN.ordinal()).isEqualTo(1);
+        assertThat(TinyFrameworkEnumComplex.GREEN.name()).isEqualTo("GREEN");
+
+        assertThat(TinyFrameworkEnumComplex.valueOf("BLUE").ordinal()).isEqualTo(2);
+
+        assertThat(TinyFrameworkEnumComplex.values()).isEqualTo(
+                new TinyFrameworkEnumComplex[] {
+                        TinyFrameworkEnumComplex.RED,
+                        TinyFrameworkEnumComplex.GREEN,
+                        TinyFrameworkEnumComplex.BLUE,
+                }
+        );
+    }
 }